summaryrefslogtreecommitdiffstats
path: root/sw/source/core/layout
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/layout')
-rw-r--r--sw/source/core/layout/anchoreddrawobject.cxx939
-rw-r--r--sw/source/core/layout/anchoredobject.cxx916
-rw-r--r--sw/source/core/layout/atrfrm.cxx3711
-rw-r--r--sw/source/core/layout/calcmove.cxx2215
-rw-r--r--sw/source/core/layout/colfrm.cxx446
-rw-r--r--sw/source/core/layout/dbg_lay.cxx953
-rw-r--r--sw/source/core/layout/dumpfilter.cxx164
-rw-r--r--sw/source/core/layout/findfrm.cxx1905
-rw-r--r--sw/source/core/layout/flowfrm.cxx2736
-rw-r--r--sw/source/core/layout/fly.cxx2997
-rw-r--r--sw/source/core/layout/flycnt.cxx1522
-rw-r--r--sw/source/core/layout/flyincnt.cxx285
-rw-r--r--sw/source/core/layout/flylay.cxx1505
-rw-r--r--sw/source/core/layout/flypos.cxx65
-rw-r--r--sw/source/core/layout/frmtool.cxx4040
-rw-r--r--sw/source/core/layout/ftnfrm.cxx2987
-rw-r--r--sw/source/core/layout/hffrm.cxx768
-rw-r--r--sw/source/core/layout/layact.cxx2419
-rw-r--r--sw/source/core/layout/laycache.cxx1200
-rw-r--r--sw/source/core/layout/layhelp.hxx224
-rw-r--r--sw/source/core/layout/layouter.cxx483
-rw-r--r--sw/source/core/layout/legacyitem.cxx78
-rw-r--r--sw/source/core/layout/movedfwdfrmsbyobjpos.cxx90
-rw-r--r--sw/source/core/layout/newfrm.cxx618
-rw-r--r--sw/source/core/layout/objectformatter.cxx478
-rw-r--r--sw/source/core/layout/objectformatterlayfrm.cxx188
-rw-r--r--sw/source/core/layout/objectformatterlayfrm.hxx70
-rw-r--r--sw/source/core/layout/objectformattertxtfrm.cxx971
-rw-r--r--sw/source/core/layout/objectformattertxtfrm.hxx190
-rw-r--r--sw/source/core/layout/objstmpconsiderwrapinfl.cxx60
-rw-r--r--sw/source/core/layout/objstmpconsiderwrapinfl.hxx42
-rw-r--r--sw/source/core/layout/pagechg.cxx2614
-rw-r--r--sw/source/core/layout/pagedesc.cxx803
-rw-r--r--sw/source/core/layout/paintfrm.cxx7721
-rw-r--r--sw/source/core/layout/sectfrm.cxx2941
-rw-r--r--sw/source/core/layout/softpagebreak.cxx154
-rw-r--r--sw/source/core/layout/sortedobjs.cxx293
-rw-r--r--sw/source/core/layout/ssfrm.cxx752
-rw-r--r--sw/source/core/layout/swselectionlist.cxx83
-rw-r--r--sw/source/core/layout/tabfrm.cxx6170
-rw-r--r--sw/source/core/layout/trvlfrm.cxx2654
-rw-r--r--sw/source/core/layout/unusedf.cxx79
-rw-r--r--sw/source/core/layout/virtoutp.cxx189
-rw-r--r--sw/source/core/layout/virtoutp.hxx61
-rw-r--r--sw/source/core/layout/wsfrm.cxx4743
45 files changed, 64522 insertions, 0 deletions
diff --git a/sw/source/core/layout/anchoreddrawobject.cxx b/sw/source/core/layout/anchoreddrawobject.cxx
new file mode 100644
index 000000000..491e86001
--- /dev/null
+++ b/sw/source/core/layout/anchoreddrawobject.cxx
@@ -0,0 +1,939 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <dcontact.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <tocntntanchoredobjectposition.hxx>
+#include <tolayoutanchoredobjectposition.hxx>
+#include <frmtool.hxx>
+#include <fmtornt.hxx>
+#include <txtfrm.hxx>
+#include <vector>
+#include <svx/svdogrp.hxx>
+#include <tools/fract.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentState.hxx>
+#include <txtfly.hxx>
+#include <viewimp.hxx>
+#include <textboxhelper.hxx>
+#include <unomid.h>
+#include <svx/svdoashp.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+/// helper class for correct notification due to the positioning of
+/// the anchored drawing object
+class SwPosNotify
+{
+ private:
+ SwAnchoredDrawObject* mpAnchoredDrawObj;
+ SwRect maOldObjRect;
+ SwPageFrame* mpOldPageFrame;
+
+ public:
+ explicit SwPosNotify( SwAnchoredDrawObject* _pAnchoredDrawObj );
+ ~SwPosNotify() COVERITY_NOEXCEPT_FALSE;
+ // #i32795#
+ Point const & LastObjPos() const;
+};
+
+}
+
+SwPosNotify::SwPosNotify( SwAnchoredDrawObject* _pAnchoredDrawObj ) :
+ mpAnchoredDrawObj( _pAnchoredDrawObj )
+{
+ maOldObjRect = mpAnchoredDrawObj->GetObjRect();
+ // --> #i35640# - determine correct page frame
+ mpOldPageFrame = mpAnchoredDrawObj->GetPageFrame();
+}
+
+SwPosNotify::~SwPosNotify() COVERITY_NOEXCEPT_FALSE
+{
+ if ( maOldObjRect != mpAnchoredDrawObj->GetObjRect() )
+ {
+ if( maOldObjRect.HasArea() && mpOldPageFrame )
+ {
+ mpAnchoredDrawObj->NotifyBackground( mpOldPageFrame, maOldObjRect,
+ PrepareHint::FlyFrameLeave );
+ }
+ SwRect aNewObjRect( mpAnchoredDrawObj->GetObjRect() );
+ if( aNewObjRect.HasArea() )
+ {
+ // --> #i35640# - determine correct page frame
+ SwPageFrame* pNewPageFrame = mpAnchoredDrawObj->GetPageFrame();
+ if( pNewPageFrame )
+ mpAnchoredDrawObj->NotifyBackground( pNewPageFrame, aNewObjRect,
+ PrepareHint::FlyFrameArrive );
+ }
+
+ ::ClrContourCache( mpAnchoredDrawObj->GetDrawObj() );
+
+ // --> #i35640# - additional notify anchor text frame
+ // Needed for negative positioned drawing objects
+ // --> #i43255# - refine condition to avoid unneeded
+ // invalidations: anchored object had to be on the page of its anchor
+ // text frame.
+ if ( mpAnchoredDrawObj->GetAnchorFrame()->IsTextFrame() &&
+ mpOldPageFrame == mpAnchoredDrawObj->GetAnchorFrame()->FindPageFrame() )
+ {
+ mpAnchoredDrawObj->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave );
+ }
+
+ // indicate a restart of the layout process
+ mpAnchoredDrawObj->SetRestartLayoutProcess( true );
+ }
+ else
+ {
+ // lock position
+ mpAnchoredDrawObj->LockPosition();
+
+ if ( !mpAnchoredDrawObj->ConsiderForTextWrap() )
+ {
+ // indicate that object has to be considered for text wrap
+ mpAnchoredDrawObj->SetConsiderForTextWrap( true );
+ // invalidate 'background' in order to allow its 'background'
+ // to wrap around it.
+ mpAnchoredDrawObj->NotifyBackground( mpAnchoredDrawObj->GetPageFrame(),
+ mpAnchoredDrawObj->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.
+ mpAnchoredDrawObj->AnchorFrame()->InvalidatePos();
+ }
+ }
+ // tdf#101464 notify SwAccessibleMap about new drawing object position
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (mpOldPageFrame && mpOldPageFrame->getRootFrame()->IsAnyShellAccessible())
+ {
+ mpOldPageFrame->getRootFrame()->GetCurrShell()->Imp()->MoveAccessible(
+ nullptr, mpAnchoredDrawObj->GetDrawObj(), maOldObjRect);
+ }
+#endif
+}
+
+// --> #i32795#
+Point const & SwPosNotify::LastObjPos() const
+{
+ return maOldObjRect.Pos();
+}
+
+namespace {
+
+// #i32795#
+/// helper class for oscillation control on object positioning
+class SwObjPosOscillationControl
+{
+ private:
+ const SwAnchoredDrawObject* mpAnchoredDrawObj;
+
+ std::vector<Point> maObjPositions;
+
+ public:
+ explicit SwObjPosOscillationControl( const SwAnchoredDrawObject& _rAnchoredDrawObj );
+
+ bool OscillationDetected();
+};
+
+}
+
+SwObjPosOscillationControl::SwObjPosOscillationControl(
+ const SwAnchoredDrawObject& _rAnchoredDrawObj )
+ : mpAnchoredDrawObj( &_rAnchoredDrawObj )
+{
+}
+
+bool SwObjPosOscillationControl::OscillationDetected()
+{
+ bool bOscillationDetected = false;
+
+ if ( maObjPositions.size() == 20 )
+ {
+ // position stack is full -> oscillation
+ bOscillationDetected = true;
+ }
+ else
+ {
+ Point aNewObjPos = mpAnchoredDrawObj->GetObjRect().Pos();
+ for ( auto const & pt : maObjPositions )
+ {
+ if ( aNewObjPos == pt )
+ {
+ // position already occurred -> oscillation
+ bOscillationDetected = true;
+ break;
+ }
+ }
+ if ( !bOscillationDetected )
+ {
+ maObjPositions.push_back( aNewObjPos );
+ }
+ }
+
+ return bOscillationDetected;
+}
+
+
+SwAnchoredDrawObject::SwAnchoredDrawObject() :
+ mbValidPos( false ),
+ mbNotYetAttachedToAnchorFrame( true ),
+ // --> #i28749#
+ mbNotYetPositioned( true ),
+ // --> #i62875#
+ mbCaptureAfterLayoutDirChange( false )
+{
+}
+
+SwAnchoredDrawObject::~SwAnchoredDrawObject()
+{
+}
+
+// --> #i62875#
+void SwAnchoredDrawObject::UpdateLayoutDir()
+{
+ SwFrameFormat::tLayoutDir nOldLayoutDir( GetFrameFormat().GetLayoutDir() );
+
+ SwAnchoredObject::UpdateLayoutDir();
+
+ if ( !NotYetPositioned() &&
+ GetFrameFormat().GetLayoutDir() != nOldLayoutDir &&
+ GetFrameFormat().GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE) &&
+ !IsOutsidePage() )
+ {
+ mbCaptureAfterLayoutDirChange = true;
+ }
+}
+
+// --> #i62875#
+bool SwAnchoredDrawObject::IsOutsidePage() const
+{
+ bool bOutsidePage( false );
+
+ if ( !NotYetPositioned() && GetPageFrame() )
+ {
+ SwRect aTmpRect( GetObjRect() );
+ bOutsidePage =
+ ( aTmpRect.Intersection( GetPageFrame()->getFrameArea() ) != GetObjRect() );
+ }
+
+ return bOutsidePage;
+}
+
+void SwAnchoredDrawObject::MakeObjPos()
+{
+ if ( IsPositioningInProgress() )
+ {
+ // nothing to do - positioning already in progress
+ return;
+ }
+
+ if ( mbValidPos )
+ {
+ // nothing to do - position is valid
+ return;
+ }
+
+ // --> #i28749# - anchored drawing object has to be attached
+ // to anchor frame
+ if ( mbNotYetAttachedToAnchorFrame )
+ {
+ OSL_FAIL( "<SwAnchoredDrawObject::MakeObjPos() - drawing object not yet attached to anchor frame -> no positioning" );
+ return;
+ }
+
+ SwDrawContact* pDrawContact =
+ static_cast<SwDrawContact*>(::GetUserCall( GetDrawObj() ));
+
+ // --> #i28749# - if anchored drawing object hasn't been yet
+ // positioned, convert its positioning attributes, if its positioning
+ // attributes are given in horizontal left-to-right layout.
+ // --> #i36010# - Note: horizontal left-to-right layout is made
+ // the default layout direction for <SwDrawFrameFormat> instances. Thus, it has
+ // to be adjusted manually, if no adjustment of the positioning attributes
+ // have to be performed here.
+ // --> #i35635# - additionally move drawing object to the visible layer.
+ if ( mbNotYetPositioned )
+ {
+ // --> #i35635#
+ pDrawContact->MoveObjToVisibleLayer( DrawObj() );
+ // --> perform conversion of positioning
+ // attributes only for 'master' drawing objects
+ // #i44334#, #i44681# - check, if positioning
+ // attributes already have been set.
+ if ( dynamic_cast< const SwDrawVirtObj* >(GetDrawObj()) == nullptr &&
+ !static_cast<SwDrawFrameFormat&>(GetFrameFormat()).IsPosAttrSet() )
+ {
+ SetPositioningAttr();
+ }
+ // -->
+ // - reset internal flag after all needed actions are performed to
+ // avoid callbacks from drawing layer
+ mbNotYetPositioned = false;
+ }
+
+ // indicate that positioning is in progress
+ {
+ SwObjPositioningInProgress aObjPosInProgress( *this );
+
+ // determine relative position of drawing object and set it
+ switch ( pDrawContact->GetAnchorId() )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ {
+ // indicate that position will be valid after positioning is performed
+ mbValidPos = true;
+ // nothing to do, because as-character anchored objects are positioned
+ // during the format of its anchor frame - see <SwFlyCntPortion::SetBase(..)>
+ }
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ // --> #i32795# - move intrinsic positioning to
+ // helper method <MakeObjPosAnchoredAtPara()>
+ MakeObjPosAnchoredAtPara();
+ }
+ break;
+ case RndStdIds::FLY_AT_PAGE:
+ case RndStdIds::FLY_AT_FLY:
+ {
+ // --> #i32795# - move intrinsic positioning to
+ // helper method <MakeObjPosAnchoredAtLayout()>
+ MakeObjPosAnchoredAtLayout();
+ }
+ break;
+ default:
+ {
+ assert(!"<SwAnchoredDrawObject::MakeObjPos()> - unknown anchor type.");
+ }
+ }
+
+ // keep, current object rectangle
+ // --> #i34748# - use new method <SetLastObjRect(..)>
+ SetLastObjRect( GetObjRect().SVRect() );
+
+ // Assure for 'master' drawing object, that it's registered at the correct page.
+ // Perform check not for as-character anchored drawing objects and only if
+ // the anchor frame is valid.
+ if ( dynamic_cast< const SwDrawVirtObj* >(GetDrawObj()) == nullptr &&
+ !pDrawContact->ObjAnchoredAsChar() &&
+ GetAnchorFrame()->isFrameAreaDefinitionValid() )
+ {
+ pDrawContact->ChkPage();
+ }
+ }
+
+ // --> #i62875#
+ if ( !(mbCaptureAfterLayoutDirChange &&
+ GetPageFrame()) )
+ return;
+
+ SwRect aPageRect( GetPageFrame()->getFrameArea() );
+ SwRect aObjRect( GetObjRect() );
+ if ( aObjRect.Right() >= aPageRect.Right() + 10 )
+ {
+ Size aSize( aPageRect.Right() - aObjRect.Right(), 0 );
+ DrawObj()->Move( aSize );
+ aObjRect = GetObjRect();
+ }
+
+ if ( aObjRect.Left() + 10 <= aPageRect.Left() )
+ {
+ Size aSize( aPageRect.Left() - aObjRect.Left(), 0 );
+ DrawObj()->Move( aSize );
+ }
+
+ mbCaptureAfterLayoutDirChange = false;
+}
+
+/** method for the intrinsic positioning of an at-paragraph|at-character
+ anchored drawing object
+
+ #i32795# - helper method for method <MakeObjPos>
+*/
+void SwAnchoredDrawObject::MakeObjPosAnchoredAtPara()
+{
+ // --> #i32795# - adopt positioning algorithm from Writer
+ // fly frames, which are anchored at paragraph|at character
+
+ // Determine, if anchor frame can/has to be formatted.
+ // If yes, after each object positioning the anchor frame is formatted.
+ // If after the anchor frame format the object position isn't valid, the
+ // object is positioned again.
+ // --> #i43255# - refine condition: anchor frame format not
+ // allowed, if another anchored object, has to be consider its wrap influence
+ // --> #i50356# - format anchor frame containing the anchor
+ // position. E.g., for at-character anchored object this can be the follow
+ // frame of the anchor frame, which contains the anchor character.
+ bool bJoinLocked
+ = static_cast<const SwTextFrame*>(GetAnchorFrameContainingAnchPos())->IsAnyJoinLocked();
+ const bool bFormatAnchor = !bJoinLocked && !ConsiderObjWrapInfluenceOnObjPos()
+ && !ConsiderObjWrapInfluenceOfOtherObjs();
+
+ // Format of anchor is needed for (vertical) fly offsets, otherwise the
+ // lack of fly portions will result in an incorrect 0 offset.
+ bool bAddVerticalFlyOffsets = GetFrameFormat().getIDocumentSettingAccess().get(
+ DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
+ bool bFormatAnchorOnce = !bJoinLocked && bAddVerticalFlyOffsets;
+
+ if (bFormatAnchor || bFormatAnchorOnce)
+ {
+ // --> #i50356#
+ GetAnchorFrameContainingAnchPos()->Calc(GetAnchorFrameContainingAnchPos()->getRootFrame()->GetCurrShell()->GetOut());
+ }
+
+ bool bOscillationDetected = false;
+ SwObjPosOscillationControl aObjPosOscCtrl( *this );
+ // --> #i3317# - boolean, to apply temporarily the
+ // 'straightforward positioning process' for the frame due to its
+ // overlapping with a previous column.
+ bool bConsiderWrapInfluenceDueToOverlapPrevCol( false );
+ do {
+ // indicate that position will be valid after positioning is performed
+ mbValidPos = true;
+
+ // --> #i35640# - correct scope for <SwPosNotify> instance
+ {
+ // create instance of <SwPosNotify> for correct notification
+ SwPosNotify aPosNotify( this );
+
+ // determine and set position
+ objectpositioning::SwToContentAnchoredObjectPosition
+ aObjPositioning( *DrawObj() );
+ aObjPositioning.CalcPosition();
+
+ // get further needed results of the positioning algorithm
+ SetVertPosOrientFrame ( aObjPositioning.GetVertPosOrientFrame() );
+ SetDrawObjAnchor();
+
+ // check for object position oscillation, if position has changed.
+ if ( GetObjRect().Pos() != aPosNotify.LastObjPos() )
+ {
+ bOscillationDetected = aObjPosOscCtrl.OscillationDetected();
+ }
+ }
+ // format anchor frame, if requested.
+ // Note: the format of the anchor frame can cause the object position
+ // to be invalid.
+ if ( bFormatAnchor )
+ {
+ // --> #i50356#
+ GetAnchorFrameContainingAnchPos()->Calc(GetAnchorFrameContainingAnchPos()->getRootFrame()->GetCurrShell()->GetOut());
+ }
+
+ // --> #i3317#
+ if ( !ConsiderObjWrapInfluenceOnObjPos() &&
+ OverlapsPrevColumn() )
+ {
+ bConsiderWrapInfluenceDueToOverlapPrevCol = true;
+ }
+ } while ( !mbValidPos && !bOscillationDetected &&
+ !bConsiderWrapInfluenceDueToOverlapPrevCol );
+
+ // --> #i3317# - consider a detected oscillation and overlapping
+ // with previous column.
+ // temporarily consider the anchored objects wrapping style influence
+ if ( bOscillationDetected || bConsiderWrapInfluenceDueToOverlapPrevCol )
+ {
+ SetTmpConsiderWrapInfluence( true );
+ SetRestartLayoutProcess( true );
+ }
+}
+
+/** method for the intrinsic positioning of an at-page|at-frame anchored
+ drawing object
+
+ #i32795# - helper method for method <MakeObjPos>
+*/
+void SwAnchoredDrawObject::MakeObjPosAnchoredAtLayout()
+{
+ // indicate that position will be valid after positioning is performed
+ mbValidPos = true;
+
+ // create instance of <SwPosNotify> for correct notification
+ SwPosNotify aPosNotify( this );
+
+ // determine position
+ objectpositioning::SwToLayoutAnchoredObjectPosition
+ aObjPositioning( *DrawObj() );
+ aObjPositioning.CalcPosition();
+
+ // set position
+
+ // --> #i31698#
+ // --> #i34995# - setting anchor position needed for filters,
+ // especially for the xml-filter to the OpenOffice.org file format
+ {
+ const Point aNewAnchorPos =
+ GetAnchorFrame()->GetFrameAnchorPos( ::HasWrap( GetDrawObj() ) );
+ DrawObj()->SetAnchorPos( aNewAnchorPos );
+ // --> #i70122# - missing invalidation
+ InvalidateObjRectWithSpaces();
+ }
+ SetCurrRelPos( aObjPositioning.GetRelPos() );
+ const SwFrame* pAnchorFrame = GetAnchorFrame();
+ SwRectFnSet aRectFnSet(pAnchorFrame);
+ const Point aAnchPos( aRectFnSet.GetPos(pAnchorFrame->getFrameArea()) );
+ SetObjLeft( aAnchPos.X() + GetCurrRelPos().X() );
+ SetObjTop( aAnchPos.Y() + GetCurrRelPos().Y() );
+}
+
+void SwAnchoredDrawObject::SetDrawObjAnchor()
+{
+ // new anchor position
+ // --> #i31698# -
+ Point aNewAnchorPos =
+ GetAnchorFrame()->GetFrameAnchorPos( ::HasWrap( GetDrawObj() ) );
+ Point aCurrAnchorPos = GetDrawObj()->GetAnchorPos();
+ if ( aNewAnchorPos != aCurrAnchorPos )
+ {
+ // determine movement to be applied after setting the new anchor position
+ Size aMove( aCurrAnchorPos.getX() - aNewAnchorPos.getX(),
+ aCurrAnchorPos.getY() - aNewAnchorPos.getY() );
+ // set new anchor position
+ DrawObj()->SetAnchorPos( aNewAnchorPos );
+ // correct object position, caused by setting new anchor position
+ DrawObj()->Move( aMove );
+ // --> #i70122# - missing invalidation
+ InvalidateObjRectWithSpaces();
+ }
+}
+
+/** method to invalidate the given page frame
+
+ #i28701#
+*/
+void SwAnchoredDrawObject::InvalidatePage_( SwPageFrame* _pPageFrame )
+{
+ if ( !_pPageFrame || _pPageFrame->GetFormat()->GetDoc()->IsInDtor() )
+ return;
+
+ if ( !_pPageFrame->GetUpper() )
+ return;
+
+ // --> #i35007# - correct invalidation for as-character
+ // anchored objects.
+ if ( GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ _pPageFrame->InvalidateFlyInCnt();
+ }
+ else
+ {
+ _pPageFrame->InvalidateFlyLayout();
+ }
+
+ SwRootFrame* pRootFrame = static_cast<SwRootFrame*>(_pPageFrame->GetUpper());
+ pRootFrame->DisallowTurbo();
+ if ( pRootFrame->GetTurbo() )
+ {
+ const SwContentFrame* pTmpFrame = pRootFrame->GetTurbo();
+ pRootFrame->ResetTurbo();
+ pTmpFrame->InvalidatePage();
+ }
+ pRootFrame->SetIdleFlags();
+}
+
+void SwAnchoredDrawObject::InvalidateObjPos()
+{
+ // --> #i28701# - check, if invalidation is allowed
+ if ( !(mbValidPos &&
+ InvalidationOfPosAllowed()) )
+ return;
+
+ mbValidPos = false;
+ // --> #i68520#
+ InvalidateObjRectWithSpaces();
+
+ // --> #i44339# - check, if anchor frame exists.
+ if ( !GetAnchorFrame() )
+ return;
+
+ // --> #118547# - notify anchor frame of as-character
+ // anchored object, because its positioned by the format of its anchor frame.
+ // --> #i44559# - assure, that text hint is already
+ // existing in the text frame
+ if ( GetAnchorFrame()->DynCastTextFrame() != nullptr &&
+ (GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) )
+ {
+ SwTextFrame* pAnchorTextFrame( static_cast<SwTextFrame*>(AnchorFrame()) );
+ if (pAnchorTextFrame->CalcFlyPos(&GetFrameFormat()) != TextFrameIndex(COMPLETE_STRING))
+ {
+ AnchorFrame()->Prepare( PrepareHint::FlyFrameAttributesChanged, &GetFrameFormat() );
+ }
+ }
+
+ SwPageFrame* pPageFrame = AnchorFrame()->FindPageFrame();
+ InvalidatePage_( pPageFrame );
+
+ // --> #i32270# - also invalidate page frame, at which the
+ // drawing object is registered at.
+ SwPageFrame* pPageFrameRegisteredAt = GetPageFrame();
+ if ( pPageFrameRegisteredAt &&
+ pPageFrameRegisteredAt != pPageFrame )
+ {
+ InvalidatePage_( pPageFrameRegisteredAt );
+ }
+ // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
+ // is replaced by method <FindPageFrameOfAnchor()>. It's return value
+ // have to be checked.
+ SwPageFrame* pPageFrameOfAnchor = FindPageFrameOfAnchor();
+ if ( pPageFrameOfAnchor &&
+ pPageFrameOfAnchor != pPageFrame &&
+ pPageFrameOfAnchor != pPageFrameRegisteredAt )
+ {
+ InvalidatePage_( pPageFrameOfAnchor );
+ }
+}
+
+SwFrameFormat& SwAnchoredDrawObject::GetFrameFormat()
+{
+ assert(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
+ return *(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
+}
+const SwFrameFormat& SwAnchoredDrawObject::GetFrameFormat() const
+{
+ assert(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
+ return *(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
+}
+
+SwRect SwAnchoredDrawObject::GetObjRect() const
+{
+ // use geometry of drawing object
+ //return GetDrawObj()->GetCurrentBoundRect();
+ return SwRect(GetDrawObj()->GetSnapRect());
+}
+
+namespace
+{
+ // Imagine an open book, inside margin is the one that is at the inner side of the pages, at the center of the book,
+ // outside margin is at the two opposite edges of the book.
+ // outside --text-- inside | inside --text-- outside
+ // With mirrored margins, when relating the size of an object from the inside margin for example, on the
+ // first page we calculate the new size of the object using the size of the right margin,
+ // on second page the left margin, third page right margin, etc.
+ tools::Long getInsideOutsideRelativeWidth(bool isOutside, const SwPageFrame* const pPageFrame)
+ {
+ // Alternating between the only two possible cases: inside and outside.
+ // Inside = false, Outside = true.
+ auto nPageNum = pPageFrame->GetPhyPageNum();
+ if (nPageNum % 2 == (isOutside ? 0 : 1))
+ return pPageFrame->GetRightMargin();
+ else
+ return pPageFrame->GetLeftMargin();
+ }
+}
+
+// --> #i70122#
+SwRect SwAnchoredDrawObject::GetObjBoundRect() const
+{
+ bool bGroupShape = dynamic_cast<const SdrObjGroup*>( GetDrawObj() );
+ // Resize objects with relative width or height
+ if ( !bGroupShape && GetPageFrame( ) && ( GetDrawObj( )->GetRelativeWidth( ) || GetDrawObj()->GetRelativeHeight( ) ) )
+ {
+ tools::Rectangle aCurrObjRect = GetDrawObj()->GetCurrentBoundRect();
+
+ tools::Long nTargetWidth = aCurrObjRect.GetWidth( );
+ if ( GetDrawObj( )->GetRelativeWidth( ) )
+ {
+ tools::Long nWidth = 0;
+ if (GetDrawObj()->GetRelativeWidthRelation() == text::RelOrientation::FRAME)
+ // Exclude margins.
+ nWidth = GetPageFrame()->getFramePrintArea().SVRect().GetWidth();
+ // Here we handle the relative size of the width of some shape.
+ // The size of the shape's width is going to be relative to the size of the left margin.
+ // E.g.: (left margin = 8 && relative size = 150%) -> width of some shape = 12.
+ else if (GetDrawObj()->GetRelativeWidthRelation() == text::RelOrientation::PAGE_LEFT)
+ {
+ if (GetPageFrame()->GetPageDesc()->GetUseOn() == UseOnPage::Mirror)
+ // We want to get the width of whatever is going through here using the size of the
+ // outside margin.
+ nWidth = getInsideOutsideRelativeWidth(true, GetPageFrame());
+ else
+ nWidth = GetPageFrame()->GetLeftMargin();
+ }
+ // Same as the left margin above.
+ else if (GetDrawObj()->GetRelativeWidthRelation() == text::RelOrientation::PAGE_RIGHT)
+ if (GetPageFrame()->GetPageDesc()->GetUseOn() == UseOnPage::Mirror)
+ // We want to get the width of whatever is going through here using the size of the
+ // inside margin.
+ nWidth = getInsideOutsideRelativeWidth(false, GetPageFrame());
+ else
+ nWidth = GetPageFrame()->GetRightMargin();
+ else
+ nWidth = GetPageFrame( )->GetBoundRect( GetPageFrame()->getRootFrame()->GetCurrShell()->GetOut() ).SVRect().GetWidth();
+ nTargetWidth = nWidth * (*GetDrawObj( )->GetRelativeWidth());
+ }
+
+ bool bCheck = GetDrawObj()->GetRelativeHeight();
+ if (bCheck)
+ {
+ auto pObjCustomShape = dynamic_cast<const SdrObjCustomShape*>(GetDrawObj());
+ bCheck = !pObjCustomShape || !pObjCustomShape->IsAutoGrowHeight();
+ }
+
+ tools::Long nTargetHeight = aCurrObjRect.GetHeight();
+ if (bCheck)
+ {
+ tools::Long nHeight = 0;
+ if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::FRAME)
+ // Exclude margins.
+ nHeight = GetPageFrame()->getFramePrintArea().SVRect().GetHeight();
+ else if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::PAGE_PRINT_AREA)
+ {
+ // count required height: print area top = top margin + header
+ SwRect aHeaderRect;
+ const SwHeaderFrame* pHeaderFrame = GetPageFrame()->GetHeaderFrame();
+ if (pHeaderFrame)
+ aHeaderRect = pHeaderFrame->GetPaintArea();
+ nHeight = GetPageFrame()->GetTopMargin() + aHeaderRect.Height();
+ }
+ else if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM)
+ {
+ // count required height: print area bottom = bottom margin + footer
+ SwRect aFooterRect;
+ auto pFooterFrame = GetPageFrame()->GetFooterFrame();
+ if (pFooterFrame)
+ aFooterRect = pFooterFrame->GetPaintArea();
+ nHeight = GetPageFrame()->GetBottomMargin() + aFooterRect.Height();
+ }
+ else
+ nHeight = GetPageFrame( )->GetBoundRect( GetPageFrame()->getRootFrame()->GetCurrShell()->GetOut() ).SVRect().GetHeight();
+ nTargetHeight = nHeight * (*GetDrawObj()->GetRelativeHeight());
+ }
+
+ if ( nTargetWidth != aCurrObjRect.GetWidth( ) || nTargetHeight != aCurrObjRect.GetHeight( ) )
+ {
+ SwDoc* pDoc = const_cast<SwDoc*>(GetPageFrame()->GetFormat()->GetDoc());
+
+ bool bEnableSetModified = pDoc->getIDocumentState().IsEnableSetModified();
+ pDoc->getIDocumentState().SetEnableSetModified(false);
+ auto pObject = const_cast<SdrObject*>(GetDrawObj());
+ pObject->Resize( aCurrObjRect.TopLeft(),
+ Fraction( nTargetWidth, aCurrObjRect.GetWidth() ),
+ Fraction( nTargetHeight, aCurrObjRect.GetHeight() ), false );
+
+ if (SwFrameFormat* pFrameFormat = FindFrameFormat(pObject))
+ {
+ if (SwTextBoxHelper::isTextBox(pFrameFormat, RES_DRAWFRMFMT))
+ {
+ // Shape has relative size and also a textbox, update its text area as well.
+ uno::Reference<drawing::XShape> xShape(pObject->getUnoShape(), uno::UNO_QUERY);
+ SwTextBoxHelper::syncProperty(pFrameFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE,
+ uno::Any(xShape->getSize()));
+ }
+ }
+
+ pDoc->getIDocumentState().SetEnableSetModified(bEnableSetModified);
+ }
+ }
+ return SwRect(GetDrawObj()->GetCurrentBoundRect());
+}
+
+// --> #i68520#
+bool SwAnchoredDrawObject::SetObjTop_( const SwTwips _nTop )
+{
+ SwTwips nDiff = _nTop - GetObjRect().Top();
+ DrawObj()->Move( Size( 0, nDiff ) );
+
+ return nDiff != 0;
+}
+bool SwAnchoredDrawObject::SetObjLeft_( const SwTwips _nLeft )
+{
+ SwTwips nDiff = _nLeft - GetObjRect().Left();
+ DrawObj()->Move( Size( nDiff, 0 ) );
+
+ return nDiff != 0;
+}
+
+/** adjust positioning and alignment attributes for new anchor frame
+
+ #i33313# - add second optional parameter <_pNewObjRect>
+*/
+void SwAnchoredDrawObject::AdjustPositioningAttr( const SwFrame* _pNewAnchorFrame,
+ const SwRect* _pNewObjRect )
+{
+ SwTwips nHoriRelPos = 0;
+ SwTwips nVertRelPos = 0;
+ const Point aAnchorPos = _pNewAnchorFrame->GetFrameAnchorPos( ::HasWrap( GetDrawObj() ) );
+ // --> #i33313#
+ const SwRect aObjRect( _pNewObjRect ? *_pNewObjRect : GetObjRect() );
+ const bool bVert = _pNewAnchorFrame->IsVertical();
+ const bool bR2L = _pNewAnchorFrame->IsRightToLeft();
+ if ( bVert )
+ {
+ nHoriRelPos = aObjRect.Top() - aAnchorPos.Y();
+ nVertRelPos = aAnchorPos.X() - aObjRect.Right();
+ }
+ else if ( bR2L )
+ {
+ nHoriRelPos = aAnchorPos.X() - aObjRect.Right();
+ nVertRelPos = aObjRect.Top() - aAnchorPos.Y();
+ }
+ else
+ {
+ nHoriRelPos = aObjRect.Left() - aAnchorPos.X();
+ nVertRelPos = aObjRect.Top() - aAnchorPos.Y();
+ }
+
+ SwFormatHoriOrient hori(nHoriRelPos, text::HoriOrientation::NONE, text::RelOrientation::FRAME);
+ SwFormatVertOrient vert(nVertRelPos, text::VertOrientation::NONE, text::RelOrientation::FRAME);
+ SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT> items(GetFrameFormat().GetDoc()->GetAttrPool());
+ items.Put(hori);
+ items.Put(vert);
+ GetFrameFormat().GetDoc()->SetAttr(items, GetFrameFormat());
+}
+
+// --> #i34748# - change return type.
+// If member <mpLastObjRect> is NULL, create one.
+void SwAnchoredDrawObject::SetLastObjRect( const tools::Rectangle& _rNewLastRect )
+{
+ maLastObjRect = _rNewLastRect;
+}
+
+void SwAnchoredDrawObject::ObjectAttachedToAnchorFrame()
+{
+ // --> #i31698#
+ SwAnchoredObject::ObjectAttachedToAnchorFrame();
+
+ if ( mbNotYetAttachedToAnchorFrame )
+ {
+ mbNotYetAttachedToAnchorFrame = false;
+ }
+}
+
+/** method to set positioning attributes
+
+ #i35798#
+ During load the positioning attributes aren't set.
+ Thus, the positioning attributes are set by the current object geometry.
+ This method is also used for the conversion for drawing objects
+ (not anchored as-character) imported from OpenOffice.org file format
+ once and directly before the first positioning.
+*/
+void SwAnchoredDrawObject::SetPositioningAttr()
+{
+ SwDrawContact* pDrawContact =
+ static_cast<SwDrawContact*>(GetUserCall( GetDrawObj() ));
+
+ if ( !pDrawContact->ObjAnchoredAsChar() )
+ {
+ SwRect aObjRect( GetObjRect() );
+
+ SwTwips nHoriPos = aObjRect.Left();
+ SwTwips nVertPos = aObjRect.Top();
+ // #i44334#, #i44681#
+ // perform conversion only if position is in horizontal-left-to-right-layout.
+ if ( GetFrameFormat().GetPositionLayoutDir() ==
+ text::PositionLayoutDir::PositionInHoriL2R )
+ {
+ SwFrameFormat::tLayoutDir eLayoutDir = GetFrameFormat().GetLayoutDir();
+ switch ( eLayoutDir )
+ {
+ case SwFrameFormat::HORI_L2R:
+ {
+ // nothing to do
+ }
+ break;
+ case SwFrameFormat::HORI_R2L:
+ {
+ nHoriPos = -aObjRect.Left() - aObjRect.Width();
+ }
+ break;
+ case SwFrameFormat::VERT_R2L:
+ {
+ nHoriPos = aObjRect.Top();
+ nVertPos = -aObjRect.Left() - aObjRect.Width();
+ }
+ break;
+ default:
+ {
+ assert(!"<SwAnchoredDrawObject::SetPositioningAttr()> - unsupported layout direction");
+ }
+ }
+ }
+
+ // --> #i71182#
+ // only change position - do not lose other attributes
+
+ SwFormatHoriOrient aHori( GetFrameFormat().GetHoriOrient() );
+ if (nHoriPos != aHori.GetPos()) {
+ aHori.SetPos( nHoriPos );
+ InvalidateObjRectWithSpaces();
+ GetFrameFormat().SetFormatAttr( aHori );
+ }
+
+ SwFormatVertOrient aVert( GetFrameFormat().GetVertOrient() );
+ if (nVertPos != aVert.GetPos()) {
+ aVert.SetPos( nVertPos );
+ InvalidateObjRectWithSpaces();
+ GetFrameFormat().SetFormatAttr( aVert );
+ }
+
+ // --> #i36010# - set layout direction of the position
+ GetFrameFormat().SetPositionLayoutDir(
+ text::PositionLayoutDir::PositionInLayoutDirOfAnchor );
+ }
+ // --> #i65798# - also for as-character anchored objects
+ // --> #i45952# - indicate that position
+ // attributes are set now.
+ static_cast<SwDrawFrameFormat&>(GetFrameFormat()).PosAttrSet();
+}
+
+void SwAnchoredDrawObject::NotifyBackground( SwPageFrame* _pPageFrame,
+ const SwRect& _rRect,
+ PrepareHint _eHint )
+{
+ ::Notify_Background( GetDrawObj(), _pPageFrame, _rRect, _eHint, true );
+}
+
+/** method to assure that anchored object is registered at the correct
+ page frame
+
+ #i28701#
+*/
+void SwAnchoredDrawObject::RegisterAtCorrectPage()
+{
+ SwPageFrame* pPageFrame( nullptr );
+ if ( GetVertPosOrientFrame() )
+ {
+ pPageFrame = const_cast<SwPageFrame*>(GetVertPosOrientFrame()->FindPageFrame());
+ }
+ if ( pPageFrame && GetPageFrame() != pPageFrame )
+ {
+ RegisterAtPage(*pPageFrame);
+ }
+}
+
+void SwAnchoredDrawObject::RegisterAtPage(SwPageFrame & rPageFrame)
+{
+ assert(GetPageFrame() != &rPageFrame);
+ if (GetPageFrame())
+ {
+ GetPageFrame()->RemoveDrawObjFromPage( *this );
+ }
+ rPageFrame.AppendDrawObjToPage( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/anchoredobject.cxx b/sw/source/core/layout/anchoredobject.cxx
new file mode 100644
index 000000000..c38cfcb03
--- /dev/null
+++ b/sw/source/core/layout/anchoredobject.cxx
@@ -0,0 +1,916 @@
+/* -*- 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 <txtfrm.hxx>
+#include <frmatr.hxx>
+#include <fmtornt.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <fmtsrnd.hxx>
+#include <dcontact.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <sortedobjs.hxx>
+#include <pagefrm.hxx>
+#include <layouter.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+// --> #i28701# -
+// implementation of helper class <SwObjPositioningInProgress>
+
+SwObjPositioningInProgress::SwObjPositioningInProgress( SdrObject& _rSdrObj ) :
+ mpAnchoredObj( nullptr ),
+ // --> #i52904#
+ mbOldObjPositioningInProgress( false )
+{
+ mpAnchoredObj = ::GetUserCall( &_rSdrObj )->GetAnchoredObj( &_rSdrObj );
+ // --> #i52904#
+ mbOldObjPositioningInProgress = mpAnchoredObj->IsPositioningInProgress();
+ mpAnchoredObj->SetPositioningInProgress( true );
+}
+SwObjPositioningInProgress::SwObjPositioningInProgress( SwAnchoredObject& _rAnchoredObj ) :
+ mpAnchoredObj( &_rAnchoredObj ),
+ // --> #i52904#
+ mbOldObjPositioningInProgress( false )
+{
+ // --> #i52904#
+ mbOldObjPositioningInProgress = mpAnchoredObj->IsPositioningInProgress();
+ mpAnchoredObj->SetPositioningInProgress( true );
+}
+
+SwObjPositioningInProgress::~SwObjPositioningInProgress()
+{
+ if ( mpAnchoredObj )
+ {
+ // --> #i52904#
+ mpAnchoredObj->SetPositioningInProgress( mbOldObjPositioningInProgress );
+ }
+}
+
+
+SwAnchoredObject::SwAnchoredObject() :
+ mpDrawObj( nullptr ),
+ mpAnchorFrame( nullptr ),
+ // --> #i28701#
+ mpPageFrame( nullptr ),
+ mnLastTopOfLine( 0 ),
+ mpVertPosOrientFrame( nullptr ),
+ // --> #i28701#
+ mbPositioningInProgress( false ),
+ mbConsiderForTextWrap( false ),
+ mbPositionLocked( false ),
+ // --> #i40147#
+ mbKeepPositionLockedForSection( false ),
+ mbRestartLayoutProcess( false ),
+ // --> #i35911#
+ mbClearedEnvironment( false ),
+ // --> #i3317#
+ mbTmpConsiderWrapInfluence( false ),
+ mbObjRectWithSpacesValid( false )
+{
+}
+
+void SwAnchoredObject::ClearVertPosOrientFrame()
+{
+ if (mpVertPosOrientFrame)
+ {
+ const_cast<SwLayoutFrame*>(mpVertPosOrientFrame)->ClearVertPosOrientFrameFor(this);
+ mpVertPosOrientFrame = nullptr;
+ }
+}
+
+SwAnchoredObject::~SwAnchoredObject()
+{
+ ClearVertPosOrientFrame();
+}
+
+void SwAnchoredObject::SetDrawObj( SdrObject& _rDrawObj )
+{
+ mpDrawObj = &_rDrawObj;
+}
+
+
+void SwAnchoredObject::ChgAnchorFrame( SwFrame* _pNewAnchorFrame )
+{
+ mpAnchorFrame = _pNewAnchorFrame;
+
+ if ( mpAnchorFrame )
+ {
+ ObjectAttachedToAnchorFrame();
+ }
+}
+
+/** determine anchor frame containing the anchor position
+
+ #i26945#
+ the anchor frame, which is determined, is <mpAnchorFrame>
+ for an at-page, at-frame or at-paragraph anchored object
+ and the anchor character frame for an at-character and as-character
+ anchored object.
+*/
+SwFrame* SwAnchoredObject::GetAnchorFrameContainingAnchPos()
+{
+ SwFrame* pAnchorFrameContainingAnchPos = FindAnchorCharFrame();
+ if ( !pAnchorFrameContainingAnchPos )
+ {
+ pAnchorFrameContainingAnchPos = AnchorFrame();
+ }
+
+ return pAnchorFrameContainingAnchPos;
+}
+
+
+void SwAnchoredObject::SetPageFrame( SwPageFrame* _pNewPageFrame )
+{
+ if ( mpPageFrame == _pNewPageFrame )
+ return;
+
+ // clear member, which denotes the layout frame at which the vertical
+ // position is oriented at, if it doesn't fit to the new page frame.
+ if ( GetVertPosOrientFrame() &&
+ ( !_pNewPageFrame ||
+ _pNewPageFrame != GetVertPosOrientFrame()->FindPageFrame() ) )
+ {
+ ClearVertPosOrientFrame();
+ }
+
+ // assign new page frame
+ mpPageFrame = _pNewPageFrame;
+}
+
+
+SwTwips SwAnchoredObject::GetRelCharX( const SwFrame* pFrame ) const
+{
+ return maLastCharRect.Left() - pFrame->getFrameArea().Left();
+}
+
+SwTwips SwAnchoredObject::GetRelCharY( const SwFrame* pFrame ) const
+{
+ return maLastCharRect.Bottom() - pFrame->getFrameArea().Top();
+}
+
+void SwAnchoredObject::AddLastCharY( tools::Long nDiff )
+{
+ maLastCharRect.Pos().AdjustY(nDiff );
+}
+
+void SwAnchoredObject::ResetLastCharRectHeight()
+{
+ maLastCharRect.Height( 0 );
+}
+
+void SwAnchoredObject::SetVertPosOrientFrame( const SwLayoutFrame& _rVertPosOrientFrame )
+{
+ ClearVertPosOrientFrame();
+
+ mpVertPosOrientFrame = &_rVertPosOrientFrame;
+ const_cast<SwLayoutFrame*>(mpVertPosOrientFrame)->SetVertPosOrientFrameFor(this);
+
+ // #i28701# - take over functionality of deleted method
+ // <SwFlyAtContentFrame::AssertPage()>: assure for at-paragraph and at-character
+ // an anchored object, that it is registered at the correct page frame
+ RegisterAtCorrectPage();
+}
+
+
+// #i28701# - follow-up of #i22341#
+void SwAnchoredObject::AddLastTopOfLineY( SwTwips _nDiff )
+{
+ mnLastTopOfLine += _nDiff;
+}
+
+/** check anchor character rectangle and top of line
+
+ #i26791
+ For to-character anchored Writer fly frames the members <maLastCharRect>
+ and <maLastTopOfLine> are updated. These are checked for change and
+ depending on the applied positioning, it's decided, if the Writer fly
+ frame has to be invalidated.
+
+ add parameter <_bCheckForParaPorInf>, default value <true>
+*/
+void SwAnchoredObject::CheckCharRectAndTopOfLine(
+ const bool _bCheckForParaPorInf )
+{
+ if ( !(GetAnchorFrame() &&
+ GetAnchorFrame()->IsTextFrame()) )
+ return;
+
+ const SwFormatAnchor& rAnch = GetFrameFormat().GetAnchor();
+ if ( !((rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) &&
+ rAnch.GetContentAnchor()) )
+ return;
+
+ // --> if requested, assure that anchor frame,
+ // which contains the anchor character, has a paragraph portion information.
+ // The paragraph portion information is needed to determine the
+ // anchor character rectangle respectively the top of the line.
+ // Thus, a format of this frame is avoided to determine the
+ // paragraph portion information.
+ // --> #i26945# - use new method <FindAnchorCharFrame()>
+ const SwTextFrame& aAnchorCharFrame = *(FindAnchorCharFrame());
+ if ( !_bCheckForParaPorInf || aAnchorCharFrame.HasPara() )
+ {
+ CheckCharRect( rAnch, aAnchorCharFrame );
+ CheckTopOfLine( rAnch, aAnchorCharFrame );
+ }
+}
+
+/** check anchor character rectangle
+
+ #i22341#
+ helper method for method <CheckCharRectAndTopOfLine()>
+ For to-character anchored Writer fly frames the member <maLastCharRect>
+ is updated. This is checked for change and depending on the applied
+ positioning, it's decided, if the Writer fly frame has to be invalidated.
+
+ improvement - add second parameter <_rAnchorCharFrame>
+*/
+void SwAnchoredObject::CheckCharRect( const SwFormatAnchor& _rAnch,
+ const SwTextFrame& _rAnchorCharFrame )
+{
+ // determine rectangle of anchor character. If not exist, abort operation
+ SwRect aCharRect;
+ if ( !_rAnchorCharFrame.GetAutoPos( aCharRect, *_rAnch.GetContentAnchor() ) )
+ {
+ return;
+ }
+ // check, if anchor character rectangle has changed
+ if ( aCharRect == maLastCharRect )
+ return;
+
+ // check positioning and alignment for invalidation of position
+ {
+ SwRectFnSet aRectFnSet(&_rAnchorCharFrame);
+ // determine positioning and alignment
+ SwFormatVertOrient aVert( GetFrameFormat().GetVertOrient() );
+ SwFormatHoriOrient aHori( GetFrameFormat().GetHoriOrient() );
+ // check for anchor character rectangle changes for certain
+ // positionings and alignments
+ // add condition to invalidate position,
+ // if vertical aligned at frame/page area and vertical position
+ // of anchor character has changed.
+ const sal_Int16 eVertRelOrient = aVert.GetRelationOrient();
+ if ( ( aHori.GetRelationOrient() == text::RelOrientation::CHAR &&
+ aRectFnSet.GetLeft(aCharRect) != aRectFnSet.GetLeft(maLastCharRect) ) ||
+ ( eVertRelOrient == text::RelOrientation::CHAR &&
+ ( aRectFnSet.GetTop(aCharRect) != aRectFnSet.GetTop(maLastCharRect) ||
+ aRectFnSet.GetHeight(aCharRect) != aRectFnSet.GetHeight(maLastCharRect) ) ) ||
+ ( ( ( eVertRelOrient == text::RelOrientation::FRAME ) ||
+ ( eVertRelOrient == text::RelOrientation::PRINT_AREA ) ||
+ ( eVertRelOrient == text::RelOrientation::PAGE_FRAME ) ||
+ ( eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA ) ) &&
+ ( aRectFnSet.GetTop(aCharRect) != aRectFnSet.GetTop(maLastCharRect) ) ) )
+ {
+ // #i26945#, #i35911# - unlock position of
+ // anchored object, if it isn't registered at the page,
+ // where its anchor character frame is on.
+ if ( GetPageFrame() != _rAnchorCharFrame.FindPageFrame() )
+ {
+ UnlockPosition();
+ }
+ InvalidateObjPos();
+ }
+ }
+ // keep new anchor character rectangle
+ maLastCharRect = aCharRect;
+}
+
+/** check top of line
+
+ #i22341#
+ helper method for method <CheckCharRectAndTopOfLine()>
+ For to-character anchored Writer fly frames the member <mnLastTopOfLine>
+ is updated. This is checked for change and depending on the applied
+ positioning, it's decided, if the Writer fly frame has to be invalidated.
+
+ improvement - add second parameter <_rAnchorCharFrame>
+*/
+void SwAnchoredObject::CheckTopOfLine( const SwFormatAnchor& _rAnch,
+ const SwTextFrame& _rAnchorCharFrame )
+{
+ SwTwips nTopOfLine = 0;
+ if ( !_rAnchorCharFrame.GetTopOfLine( nTopOfLine, *_rAnch.GetContentAnchor() ) )
+ return;
+
+ if ( nTopOfLine == mnLastTopOfLine )
+ return;
+
+ // check alignment for invalidation of position
+ if ( GetFrameFormat().GetVertOrient().GetRelationOrient() == text::RelOrientation::TEXT_LINE )
+ {
+ // #i26945#, #i35911# - unlock position of
+ // anchored object, if it isn't registered at the page,
+ // where its anchor character frame is on.
+ if ( GetPageFrame() != _rAnchorCharFrame.FindPageFrame() )
+ {
+ UnlockPosition();
+ }
+ InvalidateObjPos();
+ }
+ // keep new top of line value
+ mnLastTopOfLine = nTopOfLine;
+}
+
+void SwAnchoredObject::ClearCharRectAndTopOfLine()
+{
+ maLastCharRect.Clear();
+ mnLastTopOfLine = 0;
+}
+
+void SwAnchoredObject::SetCurrRelPos( Point _aRelPos )
+{
+ maRelPos = _aRelPos;
+}
+
+void SwAnchoredObject::ObjectAttachedToAnchorFrame()
+{
+ // default behaviour:
+ // update layout direction, the anchored object is assigned to
+ UpdateLayoutDir();
+}
+
+/** method update layout direction the layout direction, the anchored
+ object is in
+
+ #i31698#
+ method has typically to be called, if the anchored object gets its
+ anchor frame assigned.
+*/
+void SwAnchoredObject::UpdateLayoutDir()
+{
+ SwFrameFormat::tLayoutDir nLayoutDir = SwFrameFormat::HORI_L2R;
+ const SwFrame* pAnchorFrame = GetAnchorFrame();
+ if ( pAnchorFrame )
+ {
+ const bool bVert = pAnchorFrame->IsVertical();
+ const bool bR2L = pAnchorFrame->IsRightToLeft();
+ if ( bVert )
+ {
+ nLayoutDir = SwFrameFormat::VERT_R2L;
+ }
+ else if ( bR2L )
+ {
+ nLayoutDir = SwFrameFormat::HORI_R2L;
+ }
+ }
+ GetFrameFormat().SetLayoutDir( nLayoutDir );
+}
+
+/** method to perform necessary invalidations for the positioning of
+ objects, for whose the wrapping style influence has to be considered
+ on the object positioning.
+
+ #i28701#
+*/
+void SwAnchoredObject::InvalidateObjPosForConsiderWrapInfluence()
+{
+ if ( ConsiderObjWrapInfluenceOnObjPos() )
+ {
+ // indicate that object has not to be considered for text wrap
+ SetConsiderForTextWrap( false );
+ // unlock position
+ UnlockPosition();
+ // invalidate position
+ InvalidateObjPos();
+ // invalidate 'background'
+ NotifyBackground( GetPageFrame(), GetObjRectWithSpaces(), PrepareHint::FlyFrameLeave );
+ }
+}
+
+/** method to determine, if wrapping style influence of the anchored
+ object has to be considered on the object positioning
+
+ #i28701#
+ Note: result of this method also decides, if the booleans for the
+ layout process are of relevance.
+*/
+bool SwAnchoredObject::ConsiderObjWrapInfluenceOnObjPos() const
+{
+ bool bRet( false );
+
+ const SwFrameFormat& rObjFormat = GetFrameFormat();
+
+ // --> #i3317# - add condition <IsTmpConsiderWrapInfluence()>
+ // --> #i55204#
+ // - correction: wrapping style influence has been considered, if condition
+ // <IsTmpConsiderWrapInfluence()> is hold, regardless of its anchor type
+ // or its wrapping style.
+ if ( IsTmpConsiderWrapInfluence() )
+ {
+ bRet = true;
+ }
+ else if ( rObjFormat.getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ const SwFormatAnchor& rAnchor = rObjFormat.GetAnchor();
+ if ( ((rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) ||
+ (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA)) &&
+ rObjFormat.GetSurround().GetSurround() != css::text::WrapTextMode_THROUGH )
+ {
+ // --> #i34520# - text also wraps around anchored
+ // objects in the layer Hell - see the text formatting.
+ // Thus, it hasn't to be checked here.
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+/** method to determine, if other anchored objects, also attached at
+ to the anchor frame, have to consider its wrap influence.
+
+ // --> #i43255#
+*/
+bool SwAnchoredObject::ConsiderObjWrapInfluenceOfOtherObjs() const
+{
+ bool bRet( false );
+
+ const SwSortedObjs* pObjs = GetAnchorFrame()->GetDrawObjs();
+ if ( pObjs->size() > 1 )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( pAnchoredObj != this &&
+ pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool SwAnchoredObject::ConsiderForTextWrap() const
+{
+ if ( ConsiderObjWrapInfluenceOnObjPos() )
+ return mbConsiderForTextWrap;
+ else
+ return true;
+}
+
+void SwAnchoredObject::SetConsiderForTextWrap( const bool _bConsiderForTextWrap )
+{
+ mbConsiderForTextWrap = _bConsiderForTextWrap;
+}
+
+bool SwAnchoredObject::PositionLocked() const
+{
+ if ( ConsiderObjWrapInfluenceOnObjPos() )
+ return mbPositionLocked;
+ else
+ return false;
+}
+
+bool SwAnchoredObject::RestartLayoutProcess() const
+{
+ if ( ConsiderObjWrapInfluenceOnObjPos() )
+ return mbRestartLayoutProcess;
+ else
+ return false;
+}
+
+void SwAnchoredObject::SetRestartLayoutProcess( const bool _bRestartLayoutProcess )
+{
+ mbRestartLayoutProcess = _bRestartLayoutProcess;
+}
+
+// --> #i35911#
+bool SwAnchoredObject::ClearedEnvironment() const
+{
+ if ( ConsiderObjWrapInfluenceOnObjPos() )
+ return mbClearedEnvironment;
+ else
+ return false;
+}
+void SwAnchoredObject::SetClearedEnvironment( const bool _bClearedEnvironment )
+{
+ mbClearedEnvironment = _bClearedEnvironment;
+}
+
+/** method to determine, if due to anchored object size and wrapping
+ style, its layout environment is cleared.
+
+ #i35911#
+*/
+bool SwAnchoredObject::HasClearedEnvironment() const
+{
+ bool bHasClearedEnvironment( false );
+
+ // --> #i43913# - layout frame, vertical position is orient at, has to be set.
+ OSL_ENSURE( GetVertPosOrientFrame(),
+ "<SwAnchoredObject::HasClearedEnvironment()> - layout frame missing, at which the vertical position is oriented at." );
+ if ( GetVertPosOrientFrame() &&
+ GetAnchorFrame()->IsTextFrame() &&
+ !static_cast<const SwTextFrame*>(GetAnchorFrame())->IsFollow() &&
+ static_cast<const SwTextFrame*>(GetAnchorFrame())->FindPageFrame()->GetPhyPageNum() >=
+ GetPageFrame()->GetPhyPageNum() )
+ {
+ const SwFrame* pTmpFrame = GetVertPosOrientFrame()->Lower();
+ while ( pTmpFrame && pTmpFrame->IsLayoutFrame() && !pTmpFrame->IsTabFrame() )
+ {
+ pTmpFrame = static_cast<const SwLayoutFrame*>(pTmpFrame)->Lower();
+ }
+ if ( !pTmpFrame )
+ {
+ bHasClearedEnvironment = true;
+ }
+ else if ( pTmpFrame->IsTextFrame() && !pTmpFrame->GetNext() )
+ {
+ const SwTextFrame* pTmpTextFrame = static_cast<const SwTextFrame*>(pTmpFrame);
+ if ( pTmpTextFrame->IsUndersized() ||
+ ( pTmpTextFrame->GetFollow() &&
+ pTmpTextFrame->GetFollow()->GetOffset() == TextFrameIndex(0)))
+ {
+ bHasClearedEnvironment = true;
+ }
+ }
+ }
+
+ return bHasClearedEnvironment;
+}
+
+/** method to add spacing to object area
+
+ #i28701#
+ #i68520# - return constant reference and use cache
+*/
+const SwRect& SwAnchoredObject::GetObjRectWithSpaces() const
+{
+ if ( mbObjRectWithSpacesValid &&
+ maLastObjRect != GetObjRect() )
+ {
+ OSL_FAIL( "<SwAnchoredObject::GetObjRectWithSpaces> - cache for object rectangle inclusive spaces marked as valid, but it couldn't be. Missing invalidation of cache." );
+ InvalidateObjRectWithSpaces();
+ }
+ if ( !mbObjRectWithSpacesValid )
+ {
+ maObjRectWithSpaces = GetObjBoundRect();
+ const SwFrameFormat& rFormat = GetFrameFormat();
+ const SvxULSpaceItem& rUL = rFormat.GetULSpace();
+ const SvxLRSpaceItem& rLR = rFormat.GetLRSpace();
+ {
+ maObjRectWithSpaces.Top ( std::max( maObjRectWithSpaces.Top() - tools::Long(rUL.GetUpper()), tools::Long(0) ));
+ maObjRectWithSpaces.Left( std::max( maObjRectWithSpaces.Left()- rLR.GetLeft(), tools::Long(0) ));
+ maObjRectWithSpaces.AddHeight(rUL.GetLower() );
+ maObjRectWithSpaces.AddWidth(rLR.GetRight() );
+ }
+
+ mbObjRectWithSpacesValid = true;
+ maLastObjRect = GetObjRect();
+ }
+
+ return maObjRectWithSpaces;
+}
+
+// --> #i68520#
+void SwAnchoredObject::SetObjTop( const SwTwips _nTop)
+{
+ const bool bTopChanged( SetObjTop_( _nTop ) );
+ if ( bTopChanged )
+ {
+ mbObjRectWithSpacesValid = false;
+ }
+}
+
+void SwAnchoredObject::SetObjLeft( const SwTwips _nLeft)
+{
+ const bool bLeftChanged( SetObjLeft_( _nLeft ) );
+ if ( bLeftChanged )
+ {
+ mbObjRectWithSpacesValid = false;
+ }
+}
+
+/** method to update anchored object in the <SwSortedObjs> lists
+
+ #i28701#
+ If document compatibility option 'Consider wrapping style influence
+ on object positioning' is ON, additionally all anchored objects
+ at the anchor frame and all following anchored objects on the page
+ frame are invalidated.
+*/
+
+void SwAnchoredObject::UpdateObjInSortedList()
+{
+ if(!GetAnchorFrame())
+ return;
+
+ if ( GetFrameFormat().getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ // invalidate position of all anchored objects at anchor frame
+ if ( GetAnchorFrame()->GetDrawObjs() )
+ {
+ const SwSortedObjs* pObjs = GetAnchorFrame()->GetDrawObjs();
+ for(auto it = pObjs->begin(); it != pObjs->end(); ++it)
+ {
+ SwAnchoredObject* pAnchoredObj = *it;
+ if(pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos())
+ pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
+ else
+ pAnchoredObj->InvalidateObjPos();
+ }
+ }
+ // invalidate all following anchored objects on the page frame
+ if ( GetPageFrame() && GetPageFrame()->GetSortedObjs() )
+ {
+ const SwSortedObjs* pObjs = GetPageFrame()->GetSortedObjs();
+ // determine start index
+ for ( size_t i = pObjs->ListPosOf( *this ) + 1; i < pObjs->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ if ( pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() )
+ pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
+ else
+ pAnchoredObj->InvalidateObjPos();
+ }
+ }
+ }
+ // update its position in the sorted object list of its anchor frame
+ AnchorFrame()->GetDrawObjs()->Update( *this );
+ // update its position in the sorted object list of its page frame
+ // note: as-character anchored object aren't registered at a page frame
+ if ( GetPageFrame() && GetPageFrame()->GetSortedObjs() &&
+ GetFrameFormat().GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR )
+ {
+ GetPageFrame()->GetSortedObjs()->Update( *this );
+ }
+}
+
+/** method to determine, if invalidation of position is allowed
+
+ #i28701#
+*/
+bool SwAnchoredObject::InvalidationOfPosAllowed() const
+{
+ // --> Check, if page frame layout is in progress,
+ // isn't needed, because of anchored object, whose are moved forward.
+ return !PositionLocked();
+}
+
+/** method to determine the page frame, on which the 'anchor' of
+ the given anchored object is.
+
+ #i28701#
+ #i33751#, #i34060#
+ Adjust meaning of method and thus its name: If the anchored object
+ or its anchor isn't correctly inserted in the layout, no page frame
+ can be found. Thus, the return type changed to be a pointer and can
+ be NULL.
+*/
+SwPageFrame* SwAnchoredObject::FindPageFrameOfAnchor()
+{
+ SwPageFrame* pRetPageFrame = nullptr;
+
+ // --> #i44339# - check, if anchor frame exists.
+ if ( mpAnchorFrame )
+ {
+ // --> #i26945# - use new method <GetAnchorFrameContainingAnchPos()>
+ pRetPageFrame = GetAnchorFrameContainingAnchPos()->FindPageFrame();
+ }
+
+ return pRetPageFrame;
+}
+
+/** get frame, which contains the anchor character, if the object
+ is anchored at-character or as-character.
+
+ #i26945#
+
+ @return SwTextFrame*
+ text frame containing the anchor character. It's NULL, if the object
+ isn't anchored at-character resp. as-character.
+*/
+SwTextFrame* SwAnchoredObject::FindAnchorCharFrame()
+{
+ SwTextFrame* pAnchorCharFrame( nullptr );
+
+ // --> #i44339# - check, if anchor frame exists.
+ if ( mpAnchorFrame )
+ {
+ const SwFormatAnchor& rAnch = GetFrameFormat().GetAnchor();
+ if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) ||
+ (rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR))
+ {
+ SwTextFrame *const pFrame(static_cast<SwTextFrame*>(AnchorFrame()));
+ TextFrameIndex const nOffset(pFrame->MapModelToViewPos(*rAnch.GetContentAnchor()));
+ pAnchorCharFrame = &pFrame->GetFrameAtOfst(nOffset);
+ }
+ }
+
+ return pAnchorCharFrame;
+}
+
+/** method to determine, if a format on the anchored object is possible
+
+ #i28701#
+ A format is possible, if anchored object is in an invisible layer.
+ Note: method is virtual to refine the conditions for the sub-classes.
+*/
+bool SwAnchoredObject::IsFormatPossible() const
+{
+ return GetFrameFormat().GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetDrawObj()->GetLayer() );
+}
+
+bool SwAnchoredObject::IsDraggingOffPageAllowed(const SwFrameFormat* pFrameFormat)
+{
+ OSL_ASSERT(pFrameFormat);
+ const bool bDisablePositioning = pFrameFormat->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING);
+ const bool bIsWrapThrough = pFrameFormat->GetSurround().GetSurround() == text::WrapTextMode::WrapTextMode_THROUGH;
+
+ return bDisablePositioning && bIsWrapThrough;
+}
+
+// --> #i3317#
+void SwAnchoredObject::SetTmpConsiderWrapInfluence( const bool _bTmpConsiderWrapInfluence )
+{
+ mbTmpConsiderWrapInfluence = _bTmpConsiderWrapInfluence;
+ // --> #i35911#
+ if ( mbTmpConsiderWrapInfluence )
+ {
+ SwLayouter::InsertObjForTmpConsiderWrapInfluence( *(GetFrameFormat().GetDoc()),
+ *this );
+ }
+}
+
+void SwAnchoredObject::ClearTmpConsiderWrapInfluence()
+{
+ mbTmpConsiderWrapInfluence = false;
+ mbClearedEnvironment = false;
+ SetClearedEnvironment( false );
+ SwLayouter::RemoveObjForTmpConsiderWrapInfluence( *(GetFrameFormat().GetDoc()),
+ *this );
+}
+void SwAnchoredObject::SetTmpConsiderWrapInfluenceOfOtherObjs()
+{
+ const SwSortedObjs* pObjs = GetAnchorFrame()->GetDrawObjs();
+ if ( pObjs->size() > 1 )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( pAnchoredObj != this )
+ {
+ pAnchoredObj->SetTmpConsiderWrapInfluence( true/*bTmpConsiderWrapInfluence*/ );
+ }
+ }
+ }
+}
+
+/** method to determine, if the anchored object is overlapping with a
+ previous column
+
+ #i3317#
+ overlapping with a previous column means, that the object overlaps
+ with a column, which is a previous one of the column its anchor
+ frame is in.
+ Only applied for at-paragraph and at-character anchored objects.
+*/
+bool SwAnchoredObject::OverlapsPrevColumn() const
+{
+ bool bOverlapsPrevColumn( false );
+
+ if ( mpAnchorFrame && mpAnchorFrame->IsTextFrame() )
+ {
+ const SwFrame* pColFrame = mpAnchorFrame->FindColFrame();
+ if ( pColFrame && pColFrame->GetPrev() )
+ {
+ const SwFrame* pTmpColFrame = pColFrame->GetPrev();
+ SwRect aChkRect;
+ while ( pTmpColFrame )
+ {
+ aChkRect.Union( pTmpColFrame->getFrameArea() );
+ pTmpColFrame = pTmpColFrame->GetPrev();
+ }
+ bOverlapsPrevColumn = GetObjRect().Overlaps( aChkRect );
+ }
+ }
+
+ return bOverlapsPrevColumn;
+}
+
+/** method to determine position of anchored object relative to
+ anchor frame
+
+ #i30669#
+ Usage: Needed layout information for WW8 export
+*/
+Point SwAnchoredObject::GetRelPosToAnchorFrame() const
+{
+ Point aRelPos;
+
+ assert(GetAnchorFrame());
+ aRelPos = GetObjRect().Pos();
+ aRelPos -= GetAnchorFrame()->getFrameArea().Pos();
+
+ return aRelPos;
+}
+
+/** method to determine position of anchored object relative to
+ page frame
+
+ #i30669#
+ Usage: Needed layout information for WW8 export
+ #i33818# - add parameters <_bFollowTextFlow> and
+ <_obRelToTableCell>
+ If <_bFollowTextFlow> is set and object is anchored inside table,
+ the position relative to the table cell is determined. Output
+ parameter <_obRelToTableCell> reflects this situation
+*/
+Point SwAnchoredObject::GetRelPosToPageFrame( const bool _bFollowTextFlow,
+ bool& _obRelToTableCell ) const
+{
+ Point aRelPos;
+ _obRelToTableCell = false;
+
+ assert(GetAnchorFrame());
+ assert(GetAnchorFrame()->FindPageFrame());
+
+ aRelPos = GetObjRect().Pos();
+ // --> #i33818# - search for cell frame, if object has to
+ // follow the text flow.
+ const SwFrame* pFrame( nullptr );
+ if ( _bFollowTextFlow && !GetAnchorFrame()->IsPageFrame() )
+ {
+ pFrame = GetAnchorFrame()->GetUpper();
+ while ( !pFrame->IsCellFrame() && !pFrame->IsPageFrame() )
+ {
+ pFrame = pFrame->GetUpper();
+ }
+ }
+ else
+ {
+ pFrame = GetAnchorFrame()->FindPageFrame();
+ }
+ if ( pFrame->IsCellFrame() )
+ {
+ aRelPos -= pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos();
+ _obRelToTableCell = true;
+ }
+ else
+ {
+ aRelPos -= pFrame->getFrameArea().Pos();
+ }
+
+ return aRelPos;
+}
+
+/** method to determine position of anchored object relative to
+ anchor character
+
+ #i30669#
+ Usage: Needed layout information for WW8 export
+*/
+Point SwAnchoredObject::GetRelPosToChar() const
+{
+ Point aRelPos = GetObjRect().Pos();
+ aRelPos -= GetLastCharRect().Pos();
+
+ return aRelPos;
+}
+
+/** method to determine position of anchored object relative to
+ top of line
+
+ #i30669#
+ Usage: Needed layout information for WW8 export
+*/
+Point SwAnchoredObject::GetRelPosToLine() const
+{
+ Point aRelPos = GetObjRect().Pos();
+ aRelPos.AdjustY( -(GetLastTopOfLine()) );
+
+ return aRelPos;
+}
+
+const SwFlyFrame* SwAnchoredObject::DynCastFlyFrame() const
+{
+ return nullptr;
+}
+
+SwFlyFrame* SwAnchoredObject::DynCastFlyFrame()
+{
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx
new file mode 100644
index 000000000..df2d8e974
--- /dev/null
+++ b/sw/source/core/layout/atrfrm.cxx
@@ -0,0 +1,3711 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <com/sun/star/text/ColumnSeparatorStyle.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/text/TextGridMode.hpp>
+#include <com/sun/star/text/XTextColumns.hpp>
+#include <sal/log.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <svtools/unoimap.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/imap.hxx>
+#include <vcl/imapobj.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <frmfmt.hxx>
+#include <unocoll.hxx>
+#include <fmtclds.hxx>
+#include <fmtornt.hxx>
+#include <fmthdft.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtfordr.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtlsplt.hxx>
+#include <fmtrowsplt.hxx>
+#include <fmtftntx.hxx>
+#include <fmteiro.hxx>
+#include <fmturl.hxx>
+#include <fmtcnct.hxx>
+#include <section.hxx>
+#include <fmtline.hxx>
+#include <tgrditem.hxx>
+#include <hfspacingitem.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <cntfrm.hxx>
+#include <notxtfrm.hxx>
+#include <txtfrm.hxx>
+#include <crsrsh.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <frmtool.hxx>
+#include <flyfrms.hxx>
+#include <pagedesc.hxx>
+#include <grfatr.hxx>
+#include <ndnotxt.hxx>
+#include <node2lay.hxx>
+#include <fmtclbl.hxx>
+#include <swunohelper.hxx>
+#include <unoframe.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <editeng/brushitem.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <unomid.h>
+#include <strings.hrc>
+#include <svx/svdundo.hxx>
+#include <svx/SvxXTextColumns.hxx>
+#include <sortedobjs.hxx>
+#include <HandleAnchorNodeChg.hxx>
+#include <calbck.hxx>
+#include <pagedeschint.hxx>
+#include <drawdoc.hxx>
+#include <hints.hxx>
+#include <frameformats.hxx>
+#include <unoprnms.hxx>
+
+#include <ndtxt.hxx>
+
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <svl/itemiter.hxx>
+#include <wrtsh.hxx>
+#include <txtfld.hxx>
+#include <cellatr.hxx>
+
+using namespace ::com::sun::star;
+
+namespace sw {
+
+bool GetAtPageRelOrientation(sal_Int16 & rOrientation, bool const isIgnorePrintArea)
+{
+ switch (rOrientation)
+ {
+ case text::RelOrientation::CHAR:
+ case text::RelOrientation::FRAME:
+ rOrientation = text::RelOrientation::PAGE_FRAME;
+ return true;
+ case text::RelOrientation::PRINT_AREA:
+ if (isIgnorePrintArea)
+ {
+ return false;
+ }
+ else
+ {
+ rOrientation = text::RelOrientation::PAGE_PRINT_AREA;
+ return true;
+ }
+ case text::RelOrientation::FRAME_LEFT:
+ rOrientation = text::RelOrientation::PAGE_LEFT;
+ return true;
+ case text::RelOrientation::FRAME_RIGHT:
+ rOrientation = text::RelOrientation::PAGE_RIGHT;
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace sw
+
+SfxPoolItem* SwFormatLineNumber::CreateDefault() { return new SwFormatLineNumber; }
+
+static sal_Int16 lcl_IntToRelation(const uno::Any& rVal)
+{
+ sal_Int16 nVal = text::RelOrientation::FRAME;
+ if (!(rVal >>= nVal))
+ SAL_WARN("sw.core", "lcl_IntToRelation: read from Any failed!");
+ return nVal;
+}
+
+static void lcl_DelHFFormat( SwClient *pToRemove, SwFrameFormat *pFormat )
+{
+ //If the client is the last one who uses this format, then we have to delete
+ //it - before this is done, we may need to delete the content-section.
+ SwDoc* pDoc = pFormat->GetDoc();
+ pFormat->Remove( pToRemove );
+ if( pDoc->IsInDtor() )
+ {
+ delete pFormat;
+ return;
+ }
+
+ // Anything other than frames registered?
+ bool bDel = true;
+ {
+ // nested scope because DTOR of SwClientIter resets the flag bTreeChg.
+ // It's suboptimal if the format is deleted beforehand.
+ SwIterator<SwClient,SwFrameFormat> aIter(*pFormat);
+ for(SwClient* pLast = aIter.First(); bDel && pLast; pLast = aIter.Next())
+ if (dynamic_cast<const SwFrame*>(pLast) == nullptr)
+ bDel = false;
+ }
+
+ if ( !bDel )
+ return;
+
+ // If there is a Cursor registered in one of the nodes, we need to call the
+ // ParkCursor in an (arbitrary) shell.
+ SwFormatContent& rCnt = const_cast<SwFormatContent&>(pFormat->GetContent());
+ if ( rCnt.GetContentIdx() )
+ {
+ SwNode *pNode = nullptr;
+ {
+ // #i92993#
+ // Begin with start node of page header/footer to assure that
+ // complete content is checked for cursors and the complete content
+ // is deleted on below made method call <pDoc->getIDocumentContentOperations().DeleteSection(pNode)>
+ SwNodeIndex aIdx( *rCnt.GetContentIdx(), 0 );
+ // If there is a Cursor registered in one of the nodes, we need to call the
+ // ParkCursor in an (arbitrary) shell.
+ pNode = & aIdx.GetNode();
+ SwNodeOffset nEnd = pNode->EndOfSectionIndex();
+ while ( aIdx < nEnd )
+ {
+ if ( pNode->IsContentNode() &&
+ static_cast<SwContentNode*>(pNode)->HasWriterListeners() )
+ {
+ SwCursorShell *pShell = SwIterator<SwCursorShell,SwContentNode>( *static_cast<SwContentNode*>(pNode) ).First();
+ if( pShell )
+ {
+ pShell->ParkCursor( aIdx );
+ aIdx = nEnd-1;
+ }
+ }
+ ++aIdx;
+ pNode = & aIdx.GetNode();
+ }
+ }
+ rCnt.SetNewContentIdx( nullptr );
+
+ // When deleting a header/footer-format, we ALWAYS need to disable
+ // the undo function (Bug 31069)
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ OSL_ENSURE( pNode, "A big problem." );
+ pDoc->getIDocumentContentOperations().DeleteSection( pNode );
+ }
+ delete pFormat;
+}
+
+void SwFormatFrameSize::ScaleMetrics(tools::Long lMult, tools::Long lDiv) {
+ // Don't inherit the SvxSizeItem override (might or might not be relevant; added "just in case"
+ // when changing SwFormatFrameSize to derive from SvxSizeItem instead of directly from
+ // SfxPoolItem):
+ return SfxPoolItem::ScaleMetrics(lMult, lDiv);
+}
+
+bool SwFormatFrameSize::HasMetrics() const {
+ // Don't inherit the SvxSizeItem override (might or might not be relevant; added "just in case"
+ // when changing SwFormatFrameSize to derive from SvxSizeItem instead of directly from
+ // SfxPoolItem):
+ return SfxPoolItem::HasMetrics();
+}
+
+// Partially implemented inline in hxx
+SwFormatFrameSize::SwFormatFrameSize( SwFrameSize eSize, SwTwips nWidth, SwTwips nHeight )
+ : SvxSizeItem( RES_FRM_SIZE, {nWidth, nHeight} ),
+ m_eFrameHeightType( eSize ),
+ m_eFrameWidthType( SwFrameSize::Fixed )
+{
+ m_nWidthPercent = m_eWidthPercentRelation = m_nHeightPercent = m_eHeightPercentRelation = 0;
+}
+
+bool SwFormatFrameSize::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return( m_eFrameHeightType == static_cast<const SwFormatFrameSize&>(rAttr).m_eFrameHeightType &&
+ m_eFrameWidthType == static_cast<const SwFormatFrameSize&>(rAttr).m_eFrameWidthType &&
+ SvxSizeItem::operator==(rAttr)&&
+ m_nWidthPercent == static_cast<const SwFormatFrameSize&>(rAttr).GetWidthPercent() &&
+ m_eWidthPercentRelation == static_cast<const SwFormatFrameSize&>(rAttr).GetWidthPercentRelation() &&
+ m_nHeightPercent == static_cast<const SwFormatFrameSize&>(rAttr).GetHeightPercent() &&
+ m_eHeightPercentRelation == static_cast<const SwFormatFrameSize&>(rAttr).GetHeightPercentRelation() );
+}
+
+SwFormatFrameSize* SwFormatFrameSize::Clone( SfxItemPool* ) const
+{
+ return new SwFormatFrameSize( *this );
+}
+
+bool SwFormatFrameSize::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_FRMSIZE_SIZE:
+ {
+ awt::Size aTmp;
+ aTmp.Height = convertTwipToMm100(GetHeight());
+ aTmp.Width = convertTwipToMm100(GetWidth());
+ rVal <<= aTmp;
+ }
+ break;
+ case MID_FRMSIZE_REL_HEIGHT:
+ rVal <<= static_cast<sal_Int16>(GetHeightPercent() != SwFormatFrameSize::SYNCED ? GetHeightPercent() : 0);
+ break;
+ case MID_FRMSIZE_REL_HEIGHT_RELATION:
+ rVal <<= GetHeightPercentRelation();
+ break;
+ case MID_FRMSIZE_REL_WIDTH:
+ rVal <<= static_cast<sal_Int16>(GetWidthPercent() != SwFormatFrameSize::SYNCED ? GetWidthPercent() : 0);
+ break;
+ case MID_FRMSIZE_REL_WIDTH_RELATION:
+ rVal <<= GetWidthPercentRelation();
+ break;
+ case MID_FRMSIZE_IS_SYNC_HEIGHT_TO_WIDTH:
+ rVal <<= SwFormatFrameSize::SYNCED == GetHeightPercent();
+ break;
+ case MID_FRMSIZE_IS_SYNC_WIDTH_TO_HEIGHT:
+ rVal <<= SwFormatFrameSize::SYNCED == GetWidthPercent();
+ break;
+ case MID_FRMSIZE_WIDTH :
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(GetWidth()));
+ break;
+ case MID_FRMSIZE_HEIGHT:
+ // #95848# returned size should never be zero.
+ // (there was a bug that allowed for setting height to 0.
+ // Thus there some documents existing with that not allowed
+ // attribute value which may cause problems on import.)
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(GetHeight() < MINLAY ? MINLAY : GetHeight() ));
+ break;
+ case MID_FRMSIZE_SIZE_TYPE:
+ rVal <<= static_cast<sal_Int16>(GetHeightSizeType());
+ break;
+ case MID_FRMSIZE_IS_AUTO_HEIGHT:
+ rVal <<= SwFrameSize::Fixed != GetHeightSizeType();
+ break;
+ case MID_FRMSIZE_WIDTH_TYPE:
+ rVal <<= static_cast<sal_Int16>(GetWidthSizeType());
+ break;
+ }
+ return true;
+}
+
+bool SwFormatFrameSize::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0 != (nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_FRMSIZE_SIZE:
+ {
+ awt::Size aVal;
+ if(!(rVal >>= aVal))
+ bRet = false;
+ else
+ {
+ Size aTmp(aVal.Width, aVal.Height);
+ if(bConvert)
+ {
+ aTmp.setHeight(o3tl::toTwips(aTmp.Height(), o3tl::Length::mm100));
+ aTmp.setWidth(o3tl::toTwips(aTmp.Width(), o3tl::Length::mm100));
+ }
+ SetSize(aTmp);
+ }
+ }
+ break;
+ case MID_FRMSIZE_REL_HEIGHT:
+ {
+ sal_Int16 nSet = 0;
+ rVal >>= nSet;
+ if(nSet >= 0 && nSet < SwFormatFrameSize::SYNCED)
+ SetHeightPercent(static_cast<sal_uInt8>(nSet));
+ else
+ bRet = false;
+ }
+ break;
+ case MID_FRMSIZE_REL_HEIGHT_RELATION:
+ {
+ sal_Int16 eSet = 0;
+ rVal >>= eSet;
+ SetHeightPercentRelation(eSet);
+ }
+ break;
+ case MID_FRMSIZE_REL_WIDTH:
+ {
+ sal_Int16 nSet = 0;
+ rVal >>= nSet;
+ if(nSet >= 0 && nSet < SwFormatFrameSize::SYNCED)
+ SetWidthPercent(static_cast<sal_uInt8>(nSet));
+ else
+ bRet = false;
+ }
+ break;
+ case MID_FRMSIZE_REL_WIDTH_RELATION:
+ {
+ sal_Int16 eSet = 0;
+ rVal >>= eSet;
+ SetWidthPercentRelation(eSet);
+ }
+ break;
+ case MID_FRMSIZE_IS_SYNC_HEIGHT_TO_WIDTH:
+ {
+ bool bSet = *o3tl::doAccess<bool>(rVal);
+ if(bSet)
+ SetHeightPercent(SwFormatFrameSize::SYNCED);
+ else if( SwFormatFrameSize::SYNCED == GetHeightPercent() )
+ SetHeightPercent( 0 );
+ }
+ break;
+ case MID_FRMSIZE_IS_SYNC_WIDTH_TO_HEIGHT:
+ {
+ bool bSet = *o3tl::doAccess<bool>(rVal);
+ if(bSet)
+ SetWidthPercent(SwFormatFrameSize::SYNCED);
+ else if( SwFormatFrameSize::SYNCED == GetWidthPercent() )
+ SetWidthPercent(0);
+ }
+ break;
+ case MID_FRMSIZE_WIDTH :
+ {
+ sal_Int32 nWd = 0;
+ if(rVal >>= nWd)
+ {
+ if(bConvert)
+ nWd = o3tl::toTwips(nWd, o3tl::Length::mm100);
+ if(nWd < MINLAY)
+ nWd = MINLAY;
+ SetWidth(nWd);
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_FRMSIZE_HEIGHT:
+ {
+ sal_Int32 nHg = 0;
+ if(rVal >>= nHg)
+ {
+ if(bConvert)
+ nHg = o3tl::toTwips(nHg, o3tl::Length::mm100);
+ if(nHg < MINLAY)
+ nHg = MINLAY;
+ SetHeight(nHg);
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_FRMSIZE_SIZE_TYPE:
+ {
+ sal_Int16 nType = 0;
+ if((rVal >>= nType) && nType >= 0 && nType <= static_cast<int>(SwFrameSize::Minimum) )
+ {
+ SetHeightSizeType(static_cast<SwFrameSize>(nType));
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_FRMSIZE_IS_AUTO_HEIGHT:
+ {
+ bool bSet = *o3tl::doAccess<bool>(rVal);
+ SetHeightSizeType(bSet ? SwFrameSize::Variable : SwFrameSize::Fixed);
+ }
+ break;
+ case MID_FRMSIZE_WIDTH_TYPE:
+ {
+ sal_Int16 nType = 0;
+ if((rVal >>= nType) && nType >= 0 && nType <= static_cast<int>(SwFrameSize::Minimum) )
+ {
+ SetWidthSizeType(static_cast<SwFrameSize>(nType));
+ }
+ else
+ bRet = false;
+ }
+ break;
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatFrameSize::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatFrameSize"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+
+ std::stringstream aSize;
+ aSize << GetSize();
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(aSize.str().c_str()));
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eFrameHeightType"), BAD_CAST(OString::number(static_cast<int>(m_eFrameHeightType)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eFrameWidthType"), BAD_CAST(OString::number(static_cast<int>(m_eFrameWidthType)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWidthPercent"), BAD_CAST(OString::number(m_nWidthPercent).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eWidthPercentRelation"), BAD_CAST(OString::number(m_eWidthPercentRelation).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nHeightPercent"), BAD_CAST(OString::number(m_nHeightPercent).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eHeightPercentRelation"), BAD_CAST(OString::number(m_eHeightPercentRelation).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatFillOrder::SwFormatFillOrder( SwFillOrder nFO )
+ : SfxEnumItem( RES_FILL_ORDER, nFO )
+{}
+
+SwFormatFillOrder* SwFormatFillOrder::Clone( SfxItemPool* ) const
+{
+ return new SwFormatFillOrder( GetValue() );
+}
+
+sal_uInt16 SwFormatFillOrder::GetValueCount() const
+{
+ return SW_FILL_ORDER_END - SW_FILL_ORDER_BEGIN;
+}
+
+// Partially implemented inline in hxx
+SwFormatHeader::SwFormatHeader( SwFrameFormat *pHeaderFormat )
+ : SfxPoolItem( RES_HEADER ),
+ SwClient( pHeaderFormat ),
+ m_bActive( pHeaderFormat )
+{
+}
+
+SwFormatHeader::SwFormatHeader( const SwFormatHeader &rCpy )
+ : SfxPoolItem( RES_HEADER ),
+ SwClient( const_cast<sw::BroadcastingModify*>(static_cast<const sw::BroadcastingModify*>(rCpy.GetRegisteredIn())) ),
+ m_bActive( rCpy.IsActive() )
+{
+}
+
+SwFormatHeader::SwFormatHeader( bool bOn )
+ : SfxPoolItem( RES_HEADER ),
+ SwClient( nullptr ),
+ m_bActive( bOn )
+{
+}
+
+ SwFormatHeader::~SwFormatHeader()
+{
+ if ( GetHeaderFormat() )
+ lcl_DelHFFormat( this, GetHeaderFormat() );
+}
+
+bool SwFormatHeader::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( GetRegisteredIn() == static_cast<const SwFormatHeader&>(rAttr).GetRegisteredIn() &&
+ m_bActive == static_cast<const SwFormatHeader&>(rAttr).IsActive() );
+}
+
+SwFormatHeader* SwFormatHeader::Clone( SfxItemPool* ) const
+{
+ return new SwFormatHeader( *this );
+}
+
+void SwFormatHeader::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add(this);
+}
+
+// Partially implemented inline in hxx
+SwFormatFooter::SwFormatFooter( SwFrameFormat *pFooterFormat )
+ : SfxPoolItem( RES_FOOTER ),
+ SwClient( pFooterFormat ),
+ m_bActive( pFooterFormat )
+{
+}
+
+SwFormatFooter::SwFormatFooter( const SwFormatFooter &rCpy )
+ : SfxPoolItem( RES_FOOTER ),
+ SwClient( const_cast<sw::BroadcastingModify*>(static_cast<const sw::BroadcastingModify*>(rCpy.GetRegisteredIn())) ),
+ m_bActive( rCpy.IsActive() )
+{
+}
+
+SwFormatFooter::SwFormatFooter( bool bOn )
+ : SfxPoolItem( RES_FOOTER ),
+ SwClient( nullptr ),
+ m_bActive( bOn )
+{
+}
+
+ SwFormatFooter::~SwFormatFooter()
+{
+ if ( GetFooterFormat() )
+ lcl_DelHFFormat( this, GetFooterFormat() );
+}
+
+void SwFormatFooter::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add(this);
+}
+
+bool SwFormatFooter::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( GetRegisteredIn() == static_cast<const SwFormatFooter&>(rAttr).GetRegisteredIn() &&
+ m_bActive == static_cast<const SwFormatFooter&>(rAttr).IsActive() );
+}
+
+SwFormatFooter* SwFormatFooter::Clone( SfxItemPool* ) const
+{
+ return new SwFormatFooter( *this );
+}
+
+// Partially implemented inline in hxx
+SwFormatContent::SwFormatContent( const SwFormatContent &rCpy )
+ : SfxPoolItem( RES_CNTNT )
+{
+ m_pStartNode.reset( rCpy.GetContentIdx() ?
+ new SwNodeIndex( *rCpy.GetContentIdx() ) : nullptr);
+}
+
+SwFormatContent::SwFormatContent( const SwStartNode *pStartNd )
+ : SfxPoolItem( RES_CNTNT )
+{
+ m_pStartNode.reset( pStartNd ? new SwNodeIndex( *pStartNd ) : nullptr);
+}
+
+SwFormatContent::~SwFormatContent()
+{
+}
+
+void SwFormatContent::SetNewContentIdx( const SwNodeIndex *pIdx )
+{
+ m_pStartNode.reset( pIdx ? new SwNodeIndex( *pIdx ) : nullptr );
+}
+
+bool SwFormatContent::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ if( static_cast<bool>(m_pStartNode) != static_cast<bool>(static_cast<const SwFormatContent&>(rAttr).m_pStartNode) )
+ return false;
+ if( m_pStartNode )
+ return ( *m_pStartNode == *static_cast<const SwFormatContent&>(rAttr).GetContentIdx() );
+ return true;
+}
+
+SwFormatContent* SwFormatContent::Clone( SfxItemPool* ) const
+{
+ return new SwFormatContent( *this );
+}
+
+void SwFormatContent::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatContent"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ if (m_pStartNode)
+ {
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("startNode"),
+ BAD_CAST(OString::number(sal_Int32(m_pStartNode->GetNode().GetIndex())).getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("startNodePtr"), "%p",
+ &m_pStartNode->GetNode());
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatPageDesc::SwFormatPageDesc( const SwFormatPageDesc &rCpy )
+ : SfxPoolItem( RES_PAGEDESC ),
+ SwClient( const_cast<SwPageDesc*>(rCpy.GetPageDesc()) ),
+ m_oNumOffset( rCpy.m_oNumOffset ),
+ m_pDefinedIn( nullptr )
+{
+}
+
+SwFormatPageDesc::SwFormatPageDesc( const SwPageDesc *pDesc )
+ : SfxPoolItem( RES_PAGEDESC ),
+ SwClient( const_cast<SwPageDesc*>(pDesc) ),
+ m_pDefinedIn( nullptr )
+{
+}
+
+SwFormatPageDesc &SwFormatPageDesc::operator=(const SwFormatPageDesc &rCpy)
+{
+ if(this == &rCpy)
+ return *this;
+
+ if (rCpy.GetPageDesc())
+ RegisterToPageDesc(*const_cast<SwPageDesc*>(rCpy.GetPageDesc()));
+ m_oNumOffset = rCpy.m_oNumOffset;
+ m_pDefinedIn = nullptr;
+
+ return *this;
+}
+
+ SwFormatPageDesc::~SwFormatPageDesc() {}
+
+bool SwFormatPageDesc::KnowsPageDesc() const
+{
+ return (GetRegisteredIn() != nullptr);
+}
+
+bool SwFormatPageDesc::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( m_pDefinedIn == static_cast<const SwFormatPageDesc&>(rAttr).m_pDefinedIn ) &&
+ ( m_oNumOffset == static_cast<const SwFormatPageDesc&>(rAttr).m_oNumOffset ) &&
+ ( GetPageDesc() == static_cast<const SwFormatPageDesc&>(rAttr).GetPageDesc() );
+}
+
+SwFormatPageDesc* SwFormatPageDesc::Clone( SfxItemPool* ) const
+{
+ return new SwFormatPageDesc( *this );
+}
+
+void SwFormatPageDesc::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (const SwPageDescHint* pHint = dynamic_cast<const SwPageDescHint*>(&rHint))
+ {
+ // mba: shouldn't that be broadcasted also?
+ SwFormatPageDesc aDfltDesc(pHint->GetPageDesc());
+ SwPageDesc* pDesc = pHint->GetPageDesc();
+ const sw::BroadcastingModify* pMod = GetDefinedIn();
+ if(pMod)
+ {
+ if(auto pContentNode = dynamic_cast<const SwContentNode*>(pMod))
+ const_cast<SwContentNode*>(pContentNode)->SetAttr(aDfltDesc);
+ else if(auto pFormat = dynamic_cast<const SwFormat*>(pMod))
+ const_cast<SwFormat*>(pFormat)->SetFormatAttr( aDfltDesc );
+ else
+ {
+ SAL_WARN("sw.core", "SwFormatPageDesc registered at " << typeid(pMod).name() << ".");
+ RegisterToPageDesc(*pDesc);
+ }
+ }
+ else
+ // there could be an Undo-copy
+ RegisterToPageDesc(*pDesc);
+ }
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(RES_OBJECTDYING == pLegacy->GetWhich())
+ {
+ m_pDefinedIn = nullptr;
+ EndListeningAll();
+ }
+ }
+}
+
+void SwFormatPageDesc::RegisterToPageDesc( SwPageDesc& rDesc )
+{
+ rDesc.Add( this );
+}
+
+bool SwFormatPageDesc::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_PAGEDESC_PAGENUMOFFSET:
+ {
+ ::std::optional<sal_uInt16> oOffset = GetNumOffset();
+ if (oOffset)
+ {
+ rVal <<= static_cast<sal_Int16>(*oOffset);
+ }
+ else
+ {
+ rVal.clear();
+ }
+ }
+ break;
+
+ case MID_PAGEDESC_PAGEDESCNAME:
+ {
+ const SwPageDesc* pDesc = GetPageDesc();
+ if( pDesc )
+ {
+ OUString aString;
+ SwStyleNameMapper::FillProgName(pDesc->GetName(), aString, SwGetPoolIdFromName::PageDesc);
+ rVal <<= aString;
+ }
+ else
+ rVal.clear();
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatPageDesc::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_PAGEDESC_PAGENUMOFFSET:
+ {
+ sal_Int16 nOffset = 0;
+ if (!rVal.hasValue())
+ {
+ SetNumOffset(std::nullopt);
+ }
+ else if (rVal >>= nOffset)
+ SetNumOffset( nOffset );
+ else
+ bRet = false;
+ }
+ break;
+
+ case MID_PAGEDESC_PAGEDESCNAME:
+ /* Doesn't work, because the attribute doesn't need the name but a
+ * pointer to the PageDesc (it's a client of it). The pointer can
+ * only be requested from the document using the name.
+ */
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatPageDesc::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatPageDesc"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ if (m_oNumOffset)
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("oNumOffset"), BAD_CAST(OString::number(*m_oNumOffset).getStr()));
+ else
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("oNumOffset"), BAD_CAST("none"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("pPageDesc"), "%p", GetPageDesc());
+ if (const SwPageDesc* pPageDesc = GetPageDesc())
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(pPageDesc->GetName().toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SwFormatCol
+// Partially implemented inline in hxx
+
+SwColumn::SwColumn() :
+ m_nWish ( 0 ),
+ m_nLeft ( 0 ),
+ m_nRight( 0 )
+{
+}
+
+bool SwColumn::operator==( const SwColumn &rCmp ) const
+{
+ return m_nWish == rCmp.GetWishWidth() &&
+ GetLeft() == rCmp.GetLeft() &&
+ GetRight() == rCmp.GetRight();
+}
+
+void SwColumn::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwColumn"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWish"), BAD_CAST(OString::number(m_nWish).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nUpper"), BAD_CAST(OString::number(0).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLower"), BAD_CAST(OString::number(0).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLeft"), BAD_CAST(OString::number(m_nLeft).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nRight"), BAD_CAST(OString::number(m_nRight).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwFormatCol::SwFormatCol( const SwFormatCol& rCpy )
+ : SfxPoolItem( RES_COL ),
+ m_eLineStyle( rCpy.m_eLineStyle ),
+ m_nLineWidth( rCpy.m_nLineWidth),
+ m_aLineColor( rCpy.m_aLineColor),
+ m_nLineHeight( rCpy.GetLineHeight() ),
+ m_eAdj( rCpy.GetLineAdj() ),
+ m_nWidth( rCpy.GetWishWidth() ),
+ m_aWidthAdjustValue( rCpy.m_aWidthAdjustValue ),
+ m_bOrtho( rCpy.IsOrtho() )
+{
+ m_aColumns.reserve(rCpy.GetNumCols());
+ for ( sal_uInt16 i = 0; i < rCpy.GetNumCols(); ++i )
+ {
+ m_aColumns.emplace_back(rCpy.GetColumns()[i] );
+ }
+}
+
+SwFormatCol::~SwFormatCol() {}
+
+SwFormatCol& SwFormatCol::operator=( const SwFormatCol& rCpy )
+{
+ if (this != &rCpy)
+ {
+ m_eLineStyle = rCpy.m_eLineStyle;
+ m_nLineWidth = rCpy.m_nLineWidth;
+ m_aLineColor = rCpy.m_aLineColor;
+ m_nLineHeight = rCpy.GetLineHeight();
+ m_eAdj = rCpy.GetLineAdj();
+ m_nWidth = rCpy.GetWishWidth();
+ m_aWidthAdjustValue = rCpy.m_aWidthAdjustValue;
+ m_bOrtho = rCpy.IsOrtho();
+
+ m_aColumns.clear();
+ for ( sal_uInt16 i = 0; i < rCpy.GetNumCols(); ++i )
+ {
+ m_aColumns.emplace_back(rCpy.GetColumns()[i] );
+ }
+ }
+ return *this;
+}
+
+SwFormatCol::SwFormatCol()
+ : SfxPoolItem( RES_COL )
+ , m_eLineStyle( SvxBorderLineStyle::NONE)
+ ,
+ m_nLineWidth(0),
+ m_nLineHeight( 100 ),
+ m_eAdj( COLADJ_NONE ),
+ m_nWidth( USHRT_MAX ),
+ m_aWidthAdjustValue( 0 ),
+ m_bOrtho( true )
+{
+}
+
+bool SwFormatCol::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ const SwFormatCol &rCmp = static_cast<const SwFormatCol&>(rAttr);
+ if( !(m_eLineStyle == rCmp.m_eLineStyle &&
+ m_nLineWidth == rCmp.m_nLineWidth &&
+ m_aLineColor == rCmp.m_aLineColor &&
+ m_nLineHeight == rCmp.GetLineHeight() &&
+ m_eAdj == rCmp.GetLineAdj() &&
+ m_nWidth == rCmp.GetWishWidth() &&
+ m_bOrtho == rCmp.IsOrtho() &&
+ m_aColumns.size() == rCmp.GetNumCols() &&
+ m_aWidthAdjustValue == rCmp.GetAdjustValue()
+ ) )
+ return false;
+
+ for ( size_t i = 0; i < m_aColumns.size(); ++i )
+ if ( !(m_aColumns[i] == rCmp.GetColumns()[i]) )
+ return false;
+
+ return true;
+}
+
+SwFormatCol* SwFormatCol::Clone( SfxItemPool* ) const
+{
+ return new SwFormatCol( *this );
+}
+
+sal_uInt16 SwFormatCol::GetGutterWidth( bool bMin ) const
+{
+ sal_uInt16 nRet = 0;
+ if ( m_aColumns.size() == 2 )
+ nRet = m_aColumns[0].GetRight() + m_aColumns[1].GetLeft();
+ else if ( m_aColumns.size() > 2 )
+ {
+ bool bSet = false;
+ for ( size_t i = 1; i+1 < m_aColumns.size(); ++i )
+ {
+ const sal_uInt16 nTmp = m_aColumns[i].GetRight() + m_aColumns[i+1].GetLeft();
+ if ( bSet )
+ {
+ if ( nTmp != nRet )
+ {
+ if ( !bMin )
+ return USHRT_MAX;
+ if ( nRet > nTmp )
+ nRet = nTmp;
+ }
+ }
+ else
+ {
+ bSet = true;
+ nRet = nTmp;
+ }
+ }
+ }
+ return nRet;
+}
+
+void SwFormatCol::SetGutterWidth( sal_uInt16 nNew, sal_uInt16 nAct )
+{
+ if ( m_bOrtho )
+ Calc( nNew, nAct );
+ else
+ {
+ sal_uInt16 nHalf = nNew / 2;
+ for (size_t i = 0; i < m_aColumns.size(); ++i)
+ {
+ SwColumn &rCol = m_aColumns[i];
+ rCol.SetLeft(nHalf);
+ rCol.SetRight(nHalf);
+ if ( i == 0 )
+ rCol.SetLeft(0);
+ else if ( i+1 == m_aColumns.size() )
+ rCol.SetRight(0);
+ }
+ }
+}
+
+void SwFormatCol::Init( sal_uInt16 nNumCols, sal_uInt16 nGutterWidth, sal_uInt16 nAct )
+{
+ // Deleting seems to be a bit radical on the first sight; but otherwise we
+ // have to initialize all values of the remaining SwColumns.
+ m_aColumns.clear();
+ for ( sal_uInt16 i = 0; i < nNumCols; ++i )
+ {
+ m_aColumns.emplace_back( );
+ }
+ m_bOrtho = true;
+ m_nWidth = USHRT_MAX;
+ if( nNumCols )
+ Calc( nGutterWidth, nAct );
+}
+
+void SwFormatCol::SetOrtho( bool bNew, sal_uInt16 nGutterWidth, sal_uInt16 nAct )
+{
+ m_bOrtho = bNew;
+ if ( bNew && !m_aColumns.empty() )
+ Calc( nGutterWidth, nAct );
+}
+
+sal_uInt16 SwFormatCol::CalcColWidth( sal_uInt16 nCol, sal_uInt16 nAct ) const
+{
+ assert(nCol < m_aColumns.size());
+ if ( m_nWidth != nAct )
+ {
+ tools::Long nW = m_aColumns[nCol].GetWishWidth();
+ nW *= nAct;
+ nW /= m_nWidth;
+ return sal_uInt16(nW);
+ }
+ else
+ return m_aColumns[nCol].GetWishWidth();
+}
+
+sal_uInt16 SwFormatCol::CalcPrtColWidth( sal_uInt16 nCol, sal_uInt16 nAct ) const
+{
+ assert(nCol < m_aColumns.size());
+ sal_uInt16 nRet = CalcColWidth( nCol, nAct );
+ const SwColumn *pCol = &m_aColumns[nCol];
+ nRet = nRet - pCol->GetLeft();
+ nRet = nRet - pCol->GetRight();
+ return nRet;
+}
+
+void SwFormatCol::Calc( sal_uInt16 nGutterWidth, sal_uInt16 nAct )
+{
+ if (!GetNumCols())
+ return;
+
+ //First set the column widths with the current width, then calculate the
+ //column's requested width using the requested total width.
+ const sal_uInt16 nGutterHalf = nGutterWidth ? nGutterWidth / 2 : 0;
+
+ //Width of PrtAreas is totalwidth - spacings / count
+ sal_uInt16 nSpacings;
+ bool bFail = o3tl::checked_multiply<sal_uInt16>(GetNumCols() - 1, nGutterWidth, nSpacings);
+ if (bFail)
+ {
+ SAL_WARN("sw.core", "SwFormatVertOrient::Calc: overflow");
+ return;
+ }
+
+ const sal_uInt16 nPrtWidth = (nAct - nSpacings) / GetNumCols();
+ sal_uInt16 nAvail = nAct;
+
+ //The first column is PrtWidth + (gap width / 2)
+ const sal_uInt16 nLeftWidth = nPrtWidth + nGutterHalf;
+ SwColumn &rFirstCol = m_aColumns.front();
+ rFirstCol.SetWishWidth(nLeftWidth);
+ rFirstCol.SetRight(nGutterHalf);
+ rFirstCol.SetLeft(0);
+ nAvail = nAvail - nLeftWidth;
+
+ //Column 2 to n-1 is PrtWidth + gap width
+ const sal_uInt16 nMidWidth = nPrtWidth + nGutterWidth;
+
+ for (sal_uInt16 i = 1; i < GetNumCols()-1; ++i)
+ {
+ SwColumn &rCol = m_aColumns[i];
+ rCol.SetWishWidth(nMidWidth);
+ rCol.SetLeft(nGutterHalf);
+ rCol.SetRight(nGutterHalf);
+ nAvail = nAvail - nMidWidth;
+ }
+
+ //The last column is equivalent to the first one - to compensate rounding
+ //errors we add the remaining space of the other columns to the last one.
+ SwColumn &rLastCol = m_aColumns.back();
+ rLastCol.SetWishWidth(nAvail);
+ rLastCol.SetLeft(nGutterHalf);
+ rLastCol.SetRight(0);
+
+ assert(nAct != 0);
+ //Convert the current width to the requested width.
+ for (SwColumn &rCol: m_aColumns)
+ {
+ tools::Long nTmp = rCol.GetWishWidth();
+ nTmp *= GetWishWidth();
+ nTmp = nAct == 0 ? nTmp : nTmp / nAct;
+ rCol.SetWishWidth(sal_uInt16(nTmp));
+ }
+}
+
+bool SwFormatCol::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ if(MID_COLUMN_SEPARATOR_LINE == nMemberId)
+ {
+ OSL_FAIL("not implemented");
+ }
+ else
+ {
+ uno::Reference<text::XTextColumns> xCols(SvxXTextColumns_createInstance(),
+ css::uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xProps(xCols, css::uno::UNO_QUERY_THROW);
+
+ if (GetNumCols() > 0)
+ {
+ xCols->setColumnCount(GetNumCols());
+ const sal_uInt16 nItemGutterWidth = GetGutterWidth();
+ sal_Int32 nAutoDistance = IsOrtho() ? USHRT_MAX == nItemGutterWidth
+ ? DEF_GUTTER_WIDTH
+ : static_cast<sal_Int32>(nItemGutterWidth)
+ : 0;
+ nAutoDistance = convertTwipToMm100(nAutoDistance);
+ xProps->setPropertyValue(UNO_NAME_AUTOMATIC_DISTANCE, uno::Any(nAutoDistance));
+
+ if (!IsOrtho())
+ {
+ auto aTextColumns = xCols->getColumns();
+ text::TextColumn* pColumns = aTextColumns.getArray();
+ const SwColumns& rCols = GetColumns();
+ for (sal_Int32 i = 0; i < aTextColumns.getLength(); ++i)
+ {
+ const SwColumn* pCol = &rCols[i];
+
+ pColumns[i].Width = pCol->GetWishWidth();
+ pColumns[i].LeftMargin = convertTwipToMm100(pCol->GetLeft());
+ pColumns[i].RightMargin = convertTwipToMm100(pCol->GetRight());
+ }
+ xCols->setColumns(aTextColumns); // sets "IsAutomatic" property to false
+ }
+ }
+ uno::Any aVal;
+ aVal <<= o3tl::narrowing<sal_Int32>(
+ o3tl::convert(GetLineWidth(), o3tl::Length::twip, o3tl::Length::mm100));
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_WIDTH, aVal);
+ aVal <<= GetLineColor();
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_COLOR, aVal);
+ aVal <<= static_cast<sal_Int32>(GetLineHeight());
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_RELATIVE_HEIGHT, aVal);
+ aVal <<= GetLineAdj() != COLADJ_NONE;
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_IS_ON, aVal);
+ sal_Int16 nStyle;
+ switch (GetLineStyle())
+ {
+ case SvxBorderLineStyle::SOLID:
+ nStyle = css::text::ColumnSeparatorStyle::SOLID;
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ nStyle = css::text::ColumnSeparatorStyle::DOTTED;
+ break;
+ case SvxBorderLineStyle::DASHED:
+ nStyle = css::text::ColumnSeparatorStyle::DASHED;
+ break;
+ case SvxBorderLineStyle::NONE:
+ default:
+ nStyle = css::text::ColumnSeparatorStyle::NONE;
+ break;
+ }
+ aVal <<= nStyle;
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_STYLE, aVal);
+ style::VerticalAlignment eAlignment;
+ switch (GetLineAdj())
+ {
+ case COLADJ_TOP:
+ eAlignment = style::VerticalAlignment_TOP;
+ break;
+ case COLADJ_BOTTOM:
+ eAlignment = style::VerticalAlignment_BOTTOM;
+ break;
+ case COLADJ_CENTER:
+ case COLADJ_NONE:
+ default:
+ eAlignment = style::VerticalAlignment_MIDDLE;
+ }
+ aVal <<= eAlignment;
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_VERTIVAL_ALIGNMENT, aVal);
+ rVal <<= xCols;
+ }
+ return true;
+}
+
+bool SwFormatCol::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = false;
+ if(MID_COLUMN_SEPARATOR_LINE == nMemberId)
+ {
+ OSL_FAIL("not implemented");
+ }
+ else
+ {
+ uno::Reference< text::XTextColumns > xCols;
+ rVal >>= xCols;
+ if(xCols.is())
+ {
+ uno::Sequence<text::TextColumn> aSetColumns = xCols->getColumns();
+ const text::TextColumn* pArray = aSetColumns.getConstArray();
+ m_aColumns.clear();
+ //max count is 64k here - this is something the array can't do
+ sal_uInt16 nCount = std::min( o3tl::narrowing<sal_uInt16>(aSetColumns.getLength()),
+ sal_uInt16(0x3fff) );
+ sal_uInt16 nWidthSum = 0;
+ // #101224# one column is no column
+
+ if(nCount > 1)
+ for(sal_uInt16 i = 0; i < nCount; i++)
+ {
+ SwColumn aCol;
+ aCol.SetWishWidth(pArray[i].Width );
+ nWidthSum = nWidthSum + pArray[i].Width;
+ aCol.SetLeft (o3tl::toTwips(pArray[i].LeftMargin, o3tl::Length::mm100));
+ aCol.SetRight(o3tl::toTwips(pArray[i].RightMargin, o3tl::Length::mm100));
+ m_aColumns.insert(m_aColumns.begin() + i, aCol);
+ }
+ bRet = true;
+ m_nWidth = nWidthSum;
+ m_bOrtho = false;
+
+ if (uno::Reference<beans::XPropertySet> xProps{ xCols, css::uno::UNO_QUERY })
+ {
+ xProps->getPropertyValue(UNO_NAME_IS_AUTOMATIC) >>= m_bOrtho;
+ xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_WIDTH) >>= m_nLineWidth;
+ m_nLineWidth = o3tl::toTwips(m_nLineWidth, o3tl::Length::mm100);
+ xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_COLOR) >>= m_aLineColor;
+ if (sal_Int32 nHeight;
+ xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_RELATIVE_HEIGHT) >>= nHeight)
+ m_nLineHeight = nHeight;
+ switch (xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_STYLE).get<sal_Int16>())
+ {
+ default:
+ case css::text::ColumnSeparatorStyle::NONE:
+ m_eLineStyle = SvxBorderLineStyle::NONE;
+ break;
+ case css::text::ColumnSeparatorStyle::SOLID:
+ m_eLineStyle = SvxBorderLineStyle::SOLID;
+ break;
+ case css::text::ColumnSeparatorStyle::DOTTED:
+ m_eLineStyle = SvxBorderLineStyle::DOTTED;
+ break;
+ case css::text::ColumnSeparatorStyle::DASHED:
+ m_eLineStyle = SvxBorderLineStyle::DASHED;
+ break;
+ }
+ if (!xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_IS_ON).get<bool>())
+ m_eAdj = COLADJ_NONE;
+ else switch (xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_VERTIVAL_ALIGNMENT).get<style::VerticalAlignment>())
+ {
+ case style::VerticalAlignment_TOP: m_eAdj = COLADJ_TOP; break;
+ case style::VerticalAlignment_MIDDLE: m_eAdj = COLADJ_CENTER; break;
+ case style::VerticalAlignment_BOTTOM: m_eAdj = COLADJ_BOTTOM; break;
+ default: OSL_ENSURE( false, "unknown alignment" ); break;
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+void SwFormatCol::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatCol"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eLineStyle"), BAD_CAST(OString::number(static_cast<sal_Int16>(m_eLineStyle)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLineWidth"), BAD_CAST(OString::number(m_nLineWidth).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aLineColor"), BAD_CAST(m_aLineColor.AsRGBHexString().toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLineHeight"), BAD_CAST(OString::number(m_nLineHeight).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eAdj"), BAD_CAST(OString::number(m_eAdj).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWidth"), BAD_CAST(OString::number(m_nWidth).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWidthAdjustValue"), BAD_CAST(OString::number(m_aWidthAdjustValue).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bOrtho"), BAD_CAST(OString::boolean(m_bOrtho).getStr()));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("aColumns"));
+ for (const SwColumn& rColumn : m_aColumns)
+ rColumn.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatSurround::SwFormatSurround( css::text::WrapTextMode eFly ) :
+ SfxEnumItem( RES_SURROUND, eFly )
+{
+ m_bAnchorOnly = m_bContour = m_bOutside = false;
+}
+
+bool SwFormatSurround::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( GetValue() == static_cast<const SwFormatSurround&>(rAttr).GetValue() &&
+ m_bAnchorOnly== static_cast<const SwFormatSurround&>(rAttr).m_bAnchorOnly &&
+ m_bContour== static_cast<const SwFormatSurround&>(rAttr).m_bContour &&
+ m_bOutside== static_cast<const SwFormatSurround&>(rAttr).m_bOutside );
+}
+
+SwFormatSurround* SwFormatSurround::Clone( SfxItemPool* ) const
+{
+ return new SwFormatSurround( *this );
+}
+
+sal_uInt16 SwFormatSurround::GetValueCount() const
+{
+ return 6;
+}
+
+bool SwFormatSurround::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_SURROUND_SURROUNDTYPE:
+ rVal <<= GetSurround();
+ break;
+ case MID_SURROUND_ANCHORONLY:
+ rVal <<= IsAnchorOnly();
+ break;
+ case MID_SURROUND_CONTOUR:
+ rVal <<= IsContour();
+ break;
+ case MID_SURROUND_CONTOUROUTSIDE:
+ rVal <<= IsOutside();
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatSurround::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_SURROUND_SURROUNDTYPE:
+ {
+ css::text::WrapTextMode eVal = static_cast<css::text::WrapTextMode>(SWUnoHelper::GetEnumAsInt32( rVal ));
+ if( eVal >= css::text::WrapTextMode_NONE && eVal <= css::text::WrapTextMode_RIGHT )
+ SetValue( eVal );
+ else {
+ //exception
+ ;
+ }
+ }
+ break;
+
+ case MID_SURROUND_ANCHORONLY:
+ SetAnchorOnly( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_SURROUND_CONTOUR:
+ SetContour( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_SURROUND_CONTOUROUTSIDE:
+ SetOutside( *o3tl::doAccess<bool>(rVal) );
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatSurround::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatSurround"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::number(static_cast<sal_Int32>(GetValue())).getStr()));
+
+ OUString aPresentation;
+ IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
+ GetPresentation(SfxItemPresentation::Nameless, MapUnit::Map100thMM, MapUnit::Map100thMM, aPresentation, aIntlWrapper);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(aPresentation.toUtf8().getStr()));
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bAnchorOnly"), BAD_CAST(OString::boolean(m_bAnchorOnly).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bContour"), BAD_CAST(OString::boolean(m_bContour).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bOutside"), BAD_CAST(OString::boolean(m_bOutside).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatVertOrient::SwFormatVertOrient( SwTwips nY, sal_Int16 eVert,
+ sal_Int16 eRel )
+ : SfxPoolItem( RES_VERT_ORIENT ),
+ m_nYPos( nY ),
+ m_eOrient( eVert ),
+ m_eRelation( eRel )
+{}
+
+bool SwFormatVertOrient::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( m_nYPos == static_cast<const SwFormatVertOrient&>(rAttr).m_nYPos &&
+ m_eOrient == static_cast<const SwFormatVertOrient&>(rAttr).m_eOrient &&
+ m_eRelation == static_cast<const SwFormatVertOrient&>(rAttr).m_eRelation );
+}
+
+SwFormatVertOrient* SwFormatVertOrient::Clone( SfxItemPool* ) const
+{
+ return new SwFormatVertOrient( *this );
+}
+
+bool SwFormatVertOrient::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_VERTORIENT_ORIENT:
+ {
+ rVal <<= m_eOrient;
+ }
+ break;
+ case MID_VERTORIENT_RELATION:
+ rVal <<= m_eRelation;
+ break;
+ case MID_VERTORIENT_POSITION:
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(GetPos()));
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatVertOrient::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0 != (nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_VERTORIENT_ORIENT:
+ {
+ sal_uInt16 nVal = text::VertOrientation::NONE;
+ rVal >>= nVal;
+ m_eOrient = nVal;
+ }
+ break;
+ case MID_VERTORIENT_RELATION:
+ {
+ m_eRelation = lcl_IntToRelation(rVal);
+ }
+ break;
+ case MID_VERTORIENT_POSITION:
+ {
+ sal_Int32 nVal = 0;
+ rVal >>= nVal;
+ if(bConvert)
+ nVal = o3tl::toTwips(nVal, o3tl::Length::mm100);
+ SetPos( nVal );
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatVertOrient::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatVertOrient"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nYPos"), BAD_CAST(OString::number(m_nYPos).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eOrient"), BAD_CAST(OString::number(m_eOrient).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eRelation"), BAD_CAST(OString::number(m_eRelation).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatHoriOrient::SwFormatHoriOrient( SwTwips nX, sal_Int16 eHori,
+ sal_Int16 eRel, bool bPos )
+ : SfxPoolItem( RES_HORI_ORIENT ),
+ m_nXPos( nX ),
+ m_eOrient( eHori ),
+ m_eRelation( eRel ),
+ m_bPosToggle( bPos )
+{}
+
+bool SwFormatHoriOrient::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( m_nXPos == static_cast<const SwFormatHoriOrient&>(rAttr).m_nXPos &&
+ m_eOrient == static_cast<const SwFormatHoriOrient&>(rAttr).m_eOrient &&
+ m_eRelation == static_cast<const SwFormatHoriOrient&>(rAttr).m_eRelation &&
+ m_bPosToggle == static_cast<const SwFormatHoriOrient&>(rAttr).m_bPosToggle );
+}
+
+SwFormatHoriOrient* SwFormatHoriOrient::Clone( SfxItemPool* ) const
+{
+ return new SwFormatHoriOrient( *this );
+}
+
+bool SwFormatHoriOrient::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_HORIORIENT_ORIENT:
+ {
+ rVal <<= m_eOrient;
+ }
+ break;
+ case MID_HORIORIENT_RELATION:
+ rVal <<= m_eRelation;
+ break;
+ case MID_HORIORIENT_POSITION:
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(GetPos()));
+ break;
+ case MID_HORIORIENT_PAGETOGGLE:
+ rVal <<= IsPosToggle();
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatHoriOrient::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0 != (nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_HORIORIENT_ORIENT:
+ {
+ sal_Int16 nVal = text::HoriOrientation::NONE;
+ rVal >>= nVal;
+ m_eOrient = nVal;
+ }
+ break;
+ case MID_HORIORIENT_RELATION:
+ {
+ m_eRelation = lcl_IntToRelation(rVal);
+ }
+ break;
+ case MID_HORIORIENT_POSITION:
+ {
+ sal_Int32 nVal = 0;
+ if(!(rVal >>= nVal))
+ bRet = false;
+ if(bConvert)
+ nVal = o3tl::toTwips(nVal, o3tl::Length::mm100);
+ SetPos( nVal );
+ }
+ break;
+ case MID_HORIORIENT_PAGETOGGLE:
+ SetPosToggle( *o3tl::doAccess<bool>(rVal));
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatHoriOrient::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatHoriOrient"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nXPos"), BAD_CAST(OString::number(m_nXPos).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eOrient"), BAD_CAST(OString::number(m_eOrient).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eRelation"), BAD_CAST(OString::number(m_eRelation).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bPosToggle"), BAD_CAST(OString::boolean(m_bPosToggle).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatAnchor::SwFormatAnchor( RndStdIds nRnd, sal_uInt16 nPage )
+ : SfxPoolItem( RES_ANCHOR ),
+ m_eAnchorId( nRnd ),
+ m_nPageNumber( nPage ),
+ // OD 2004-05-05 #i28701# - get always new increased order number
+ m_nOrder( ++s_nOrderCounter )
+{}
+
+SwFormatAnchor::SwFormatAnchor( const SwFormatAnchor &rCpy )
+ : SfxPoolItem( RES_ANCHOR )
+ , m_pContentAnchor( (rCpy.GetContentAnchor())
+ ? new SwPosition( *rCpy.GetContentAnchor() ) : nullptr )
+ , m_eAnchorId( rCpy.GetAnchorId() )
+ , m_nPageNumber( rCpy.GetPageNum() )
+ // OD 2004-05-05 #i28701# - get always new increased order number
+ , m_nOrder( ++s_nOrderCounter )
+{
+}
+
+SwFormatAnchor::~SwFormatAnchor()
+{
+}
+
+void SwFormatAnchor::SetAnchor( const SwPosition *pPos )
+{
+ // anchor only to paragraphs, or start nodes in case of RndStdIds::FLY_AT_FLY
+ // also allow table node, this is used when a table is selected and is converted to a frame by the UI
+ assert(!pPos
+ || ((RndStdIds::FLY_AT_FLY == m_eAnchorId) &&
+ dynamic_cast<SwStartNode*>(&pPos->nNode.GetNode()))
+ || (RndStdIds::FLY_AT_PARA == m_eAnchorId && dynamic_cast<SwTableNode*>(&pPos->nNode.GetNode()))
+ || dynamic_cast<SwTextNode*>(&pPos->nNode.GetNode()));
+ m_pContentAnchor .reset( pPos ? new SwPosition( *pPos ) : nullptr );
+ // Flys anchored AT paragraph should not point into the paragraph content
+ if (m_pContentAnchor &&
+ ((RndStdIds::FLY_AT_PARA == m_eAnchorId) || (RndStdIds::FLY_AT_FLY == m_eAnchorId)))
+ {
+ m_pContentAnchor->nContent.Assign( nullptr, 0 );
+ }
+}
+
+SwFormatAnchor& SwFormatAnchor::operator=(const SwFormatAnchor& rAnchor)
+{
+ if (this != &rAnchor)
+ {
+ m_eAnchorId = rAnchor.GetAnchorId();
+ m_nPageNumber = rAnchor.GetPageNum();
+ // OD 2004-05-05 #i28701# - get always new increased order number
+ m_nOrder = ++s_nOrderCounter;
+
+ m_pContentAnchor.reset( (rAnchor.GetContentAnchor())
+ ? new SwPosition(*(rAnchor.GetContentAnchor()))
+ : nullptr );
+ }
+ return *this;
+}
+
+bool SwFormatAnchor::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ SwFormatAnchor const& rFormatAnchor(static_cast<SwFormatAnchor const&>(rAttr));
+ // OD 2004-05-05 #i28701# - Note: <mnOrder> hasn't to be considered.
+ return ( m_eAnchorId == rFormatAnchor.GetAnchorId() &&
+ m_nPageNumber == rFormatAnchor.GetPageNum() &&
+ // compare anchor: either both do not point into a textnode or
+ // both do (valid m_pContentAnchor) and the positions are equal
+ ((m_pContentAnchor.get() == rFormatAnchor.m_pContentAnchor.get()) ||
+ (m_pContentAnchor && rFormatAnchor.GetContentAnchor() &&
+ (*m_pContentAnchor == *rFormatAnchor.GetContentAnchor()))));
+}
+
+SwFormatAnchor* SwFormatAnchor::Clone( SfxItemPool* ) const
+{
+ return new SwFormatAnchor( *this );
+}
+
+// OD 2004-05-05 #i28701#
+sal_uInt32 SwFormatAnchor::s_nOrderCounter = 0;
+
+// OD 2004-05-05 #i28701#
+
+bool SwFormatAnchor::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_ANCHOR_ANCHORTYPE:
+
+ text::TextContentAnchorType eRet;
+ switch (GetAnchorId())
+ {
+ case RndStdIds::FLY_AT_CHAR:
+ eRet = text::TextContentAnchorType_AT_CHARACTER;
+ break;
+ case RndStdIds::FLY_AT_PAGE:
+ eRet = text::TextContentAnchorType_AT_PAGE;
+ break;
+ case RndStdIds::FLY_AT_FLY:
+ eRet = text::TextContentAnchorType_AT_FRAME;
+ break;
+ case RndStdIds::FLY_AS_CHAR:
+ eRet = text::TextContentAnchorType_AS_CHARACTER;
+ break;
+ //case RndStdIds::FLY_AT_PARA:
+ default:
+ eRet = text::TextContentAnchorType_AT_PARAGRAPH;
+ }
+ rVal <<= eRet;
+ break;
+ case MID_ANCHOR_PAGENUM:
+ rVal <<= static_cast<sal_Int16>(GetPageNum());
+ break;
+ case MID_ANCHOR_ANCHORFRAME:
+ {
+ if (m_pContentAnchor && RndStdIds::FLY_AT_FLY == m_eAnchorId)
+ {
+ SwFrameFormat* pFormat = m_pContentAnchor->nNode.GetNode().GetFlyFormat();
+ if(pFormat)
+ {
+ uno::Reference<text::XTextFrame> const xRet(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat));
+ rVal <<= xRet;
+ }
+ }
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatAnchor::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_ANCHOR_ANCHORTYPE:
+ {
+ RndStdIds eAnchor;
+ switch( static_cast<text::TextContentAnchorType>(SWUnoHelper::GetEnumAsInt32( rVal )) )
+ {
+ case text::TextContentAnchorType_AS_CHARACTER:
+ eAnchor = RndStdIds::FLY_AS_CHAR;
+ break;
+ case text::TextContentAnchorType_AT_PAGE:
+ eAnchor = RndStdIds::FLY_AT_PAGE;
+ if( GetPageNum() > 0 )
+ {
+ // If the anchor type is page and a valid page number
+ // has been set, the content position isn't required
+ // any longer.
+ m_pContentAnchor.reset();
+ }
+ break;
+ case text::TextContentAnchorType_AT_FRAME:
+ eAnchor = RndStdIds::FLY_AT_FLY;
+ break;
+ case text::TextContentAnchorType_AT_CHARACTER:
+ eAnchor = RndStdIds::FLY_AT_CHAR;
+ break;
+ //case text::TextContentAnchorType_AT_PARAGRAPH:
+ default:
+ eAnchor = RndStdIds::FLY_AT_PARA;
+ break;
+ }
+ SetType( eAnchor );
+ }
+ break;
+ case MID_ANCHOR_PAGENUM:
+ {
+ sal_Int16 nVal = 0;
+ if((rVal >>= nVal) && nVal > 0)
+ {
+ SetPageNum( nVal );
+ if (RndStdIds::FLY_AT_PAGE == GetAnchorId())
+ {
+ // If the anchor type is page and a valid page number
+ // is set, the content position has to be deleted to not
+ // confuse the layout (frmtool.cxx). However, if the
+ // anchor type is not page, any content position will
+ // be kept.
+ m_pContentAnchor.reset();
+ }
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_ANCHOR_ANCHORFRAME:
+ //no break here!;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatAnchor::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatAnchor"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+
+ if (m_pContentAnchor)
+ {
+ std::stringstream aContentAnchor;
+ aContentAnchor << *m_pContentAnchor;
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_pContentAnchor"), BAD_CAST(aContentAnchor.str().c_str()));
+ }
+ else
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pContentAnchor"), "%p", m_pContentAnchor.get());
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_eAnchorType"), BAD_CAST(OString::number(static_cast<int>(m_eAnchorId)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nPageNumber"), BAD_CAST(OString::number(m_nPageNumber).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nOrder"), BAD_CAST(OString::number(m_nOrder).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("s_nOrderCounter"), BAD_CAST(OString::number(s_nOrderCounter).getStr()));
+ OUString aPresentation;
+ IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
+ GetPresentation(SfxItemPresentation::Nameless, MapUnit::Map100thMM, MapUnit::Map100thMM, aPresentation, aIntlWrapper);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(aPresentation.toUtf8().getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatURL::SwFormatURL() :
+ SfxPoolItem( RES_URL ),
+ m_bIsServerMap( false )
+{
+}
+
+SwFormatURL::SwFormatURL( const SwFormatURL &rURL) :
+ SfxPoolItem( RES_URL ),
+ m_sTargetFrameName( rURL.GetTargetFrameName() ),
+ m_sURL( rURL.GetURL() ),
+ m_sName( rURL.GetName() ),
+ m_bIsServerMap( rURL.IsServerMap() )
+{
+ if (rURL.GetMap())
+ m_pMap.reset( new ImageMap( *rURL.GetMap() ) );
+}
+
+SwFormatURL::~SwFormatURL()
+{
+}
+
+bool SwFormatURL::operator==( const SfxPoolItem &rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ const SwFormatURL &rCmp = static_cast<const SwFormatURL&>(rAttr);
+ bool bRet = m_bIsServerMap == rCmp.IsServerMap() &&
+ m_sURL == rCmp.GetURL() &&
+ m_sTargetFrameName == rCmp.GetTargetFrameName() &&
+ m_sName == rCmp.GetName();
+ if ( bRet )
+ {
+ if ( m_pMap && rCmp.GetMap() )
+ bRet = *m_pMap == *rCmp.GetMap();
+ else
+ bRet = m_pMap.get() == rCmp.GetMap();
+ }
+ return bRet;
+}
+
+SwFormatURL* SwFormatURL::Clone( SfxItemPool* ) const
+{
+ return new SwFormatURL( *this );
+}
+
+void SwFormatURL::SetURL(const OUString &rURL, bool bServerMap)
+{
+ m_sURL = rURL;
+ m_bIsServerMap = bServerMap;
+}
+
+void SwFormatURL::SetMap( const ImageMap *pM )
+{
+ m_pMap.reset( pM ? new ImageMap( *pM ) : nullptr);
+}
+
+bool SwFormatURL::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_URL_URL:
+ rVal <<= GetURL();
+ break;
+ case MID_URL_TARGET:
+ rVal <<= GetTargetFrameName();
+ break;
+ case MID_URL_HYPERLINKNAME:
+ rVal <<= GetName();
+ break;
+ case MID_URL_CLIENTMAP:
+ {
+ uno::Reference< uno::XInterface > xInt;
+ if(m_pMap)
+ {
+ xInt = SvUnoImageMap_createInstance( *m_pMap, sw_GetSupportedMacroItems() );
+ }
+ else
+ {
+ ImageMap aEmptyMap;
+ xInt = SvUnoImageMap_createInstance( aEmptyMap, sw_GetSupportedMacroItems() );
+ }
+ uno::Reference< container::XIndexContainer > xCont(xInt, uno::UNO_QUERY);
+ rVal <<= xCont;
+ }
+ break;
+ case MID_URL_SERVERMAP:
+ rVal <<= IsServerMap();
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatURL::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_URL_URL:
+ {
+ OUString sTmp;
+ rVal >>= sTmp;
+ SetURL( sTmp, m_bIsServerMap );
+ }
+ break;
+ case MID_URL_TARGET:
+ {
+ OUString sTmp;
+ rVal >>= sTmp;
+ SetTargetFrameName( sTmp );
+ }
+ break;
+ case MID_URL_HYPERLINKNAME:
+ {
+ OUString sTmp;
+ rVal >>= sTmp;
+ SetName( sTmp );
+ }
+ break;
+ case MID_URL_CLIENTMAP:
+ {
+ uno::Reference<container::XIndexContainer> xCont;
+ if(!rVal.hasValue())
+ m_pMap.reset();
+ else if(rVal >>= xCont)
+ {
+ if(!m_pMap)
+ m_pMap.reset(new ImageMap);
+ bRet = SvUnoImageMap_fillImageMap( xCont, *m_pMap );
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_URL_SERVERMAP:
+ m_bIsServerMap = *o3tl::doAccess<bool>(rVal);
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+SwFormatEditInReadonly* SwFormatEditInReadonly::Clone( SfxItemPool* ) const
+{
+ return new SwFormatEditInReadonly( *this );
+}
+
+SwFormatLayoutSplit* SwFormatLayoutSplit::Clone( SfxItemPool* ) const
+{
+ return new SwFormatLayoutSplit( *this );
+}
+
+SwFormatRowSplit* SwFormatRowSplit::Clone( SfxItemPool* ) const
+{
+ return new SwFormatRowSplit( *this );
+}
+
+SwFormatNoBalancedColumns* SwFormatNoBalancedColumns::Clone( SfxItemPool* ) const
+{
+ return new SwFormatNoBalancedColumns( *this );
+}
+
+void SwFormatNoBalancedColumns::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatNoBalancedColumns"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SwFormatFootnoteEndAtTextEnd
+
+sal_uInt16 SwFormatFootnoteEndAtTextEnd::GetValueCount() const
+{
+ return sal_uInt16( FTNEND_ATTXTEND_END );
+}
+
+SwFormatFootnoteEndAtTextEnd& SwFormatFootnoteEndAtTextEnd::operator=(
+ const SwFormatFootnoteEndAtTextEnd& rAttr )
+{
+ SfxEnumItem::SetValue( rAttr.GetValue() );
+ m_aFormat = rAttr.m_aFormat;
+ m_nOffset = rAttr.m_nOffset;
+ m_sPrefix = rAttr.m_sPrefix;
+ m_sSuffix = rAttr.m_sSuffix;
+ return *this;
+}
+
+bool SwFormatFootnoteEndAtTextEnd::operator==( const SfxPoolItem& rItem ) const
+{
+ const SwFormatFootnoteEndAtTextEnd& rAttr = static_cast<const SwFormatFootnoteEndAtTextEnd&>(rItem);
+ return SfxEnumItem::operator==( rItem ) &&
+ m_aFormat.GetNumberingType() == rAttr.m_aFormat.GetNumberingType() &&
+ m_nOffset == rAttr.m_nOffset &&
+ m_sPrefix == rAttr.m_sPrefix &&
+ m_sSuffix == rAttr.m_sSuffix;
+}
+
+bool SwFormatFootnoteEndAtTextEnd::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_COLLECT :
+ rVal <<= GetValue() >= FTNEND_ATTXTEND;
+ break;
+ case MID_RESTART_NUM :
+ rVal <<= GetValue() >= FTNEND_ATTXTEND_OWNNUMSEQ;
+ break;
+ case MID_NUM_START_AT: rVal <<= static_cast<sal_Int16>(m_nOffset); break;
+ case MID_OWN_NUM :
+ rVal <<= GetValue() >= FTNEND_ATTXTEND_OWNNUMANDFMT;
+ break;
+ case MID_NUM_TYPE : rVal <<= static_cast<sal_Int16>(m_aFormat.GetNumberingType()); break;
+ case MID_PREFIX : rVal <<= m_sPrefix; break;
+ case MID_SUFFIX : rVal <<= m_sSuffix; break;
+ default: return false;
+ }
+ return true;
+}
+
+bool SwFormatFootnoteEndAtTextEnd::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bRet = true;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_COLLECT :
+ {
+ bool bVal = *o3tl::doAccess<bool>(rVal);
+ if(!bVal && GetValue() >= FTNEND_ATTXTEND)
+ SetValue(FTNEND_ATPGORDOCEND);
+ else if(bVal && GetValue() < FTNEND_ATTXTEND)
+ SetValue(FTNEND_ATTXTEND);
+ }
+ break;
+ case MID_RESTART_NUM :
+ {
+ bool bVal = *o3tl::doAccess<bool>(rVal);
+ if(!bVal && GetValue() >= FTNEND_ATTXTEND_OWNNUMSEQ)
+ SetValue(FTNEND_ATTXTEND);
+ else if(bVal && GetValue() < FTNEND_ATTXTEND_OWNNUMSEQ)
+ SetValue(FTNEND_ATTXTEND_OWNNUMSEQ);
+ }
+ break;
+ case MID_NUM_START_AT:
+ {
+ sal_Int16 nVal = 0;
+ rVal >>= nVal;
+ if(nVal >= 0)
+ m_nOffset = nVal;
+ else
+ bRet = false;
+ }
+ break;
+ case MID_OWN_NUM :
+ {
+ bool bVal = *o3tl::doAccess<bool>(rVal);
+ if(!bVal && GetValue() >= FTNEND_ATTXTEND_OWNNUMANDFMT)
+ SetValue(FTNEND_ATTXTEND_OWNNUMSEQ);
+ else if(bVal && GetValue() < FTNEND_ATTXTEND_OWNNUMANDFMT)
+ SetValue(FTNEND_ATTXTEND_OWNNUMANDFMT);
+ }
+ break;
+ case MID_NUM_TYPE :
+ {
+ sal_Int16 nVal = 0;
+ rVal >>= nVal;
+ if(nVal >= 0 &&
+ (nVal <= SVX_NUM_ARABIC ||
+ SVX_NUM_CHARS_UPPER_LETTER_N == nVal ||
+ SVX_NUM_CHARS_LOWER_LETTER_N == nVal ))
+ m_aFormat.SetNumberingType(static_cast<SvxNumType>(nVal));
+ else
+ bRet = false;
+ }
+ break;
+ case MID_PREFIX :
+ {
+ OUString sVal; rVal >>= sVal;
+ m_sPrefix = sVal;
+ }
+ break;
+ case MID_SUFFIX :
+ {
+ OUString sVal; rVal >>= sVal;
+ m_sSuffix = sVal;
+ }
+ break;
+ default: bRet = false;
+ }
+ return bRet;
+}
+
+// class SwFormatFootnoteAtTextEnd
+
+SwFormatFootnoteAtTextEnd* SwFormatFootnoteAtTextEnd::Clone( SfxItemPool* ) const
+{
+ return new SwFormatFootnoteAtTextEnd(*this);
+}
+
+// class SwFormatEndAtTextEnd
+
+SwFormatEndAtTextEnd* SwFormatEndAtTextEnd::Clone( SfxItemPool* ) const
+{
+ return new SwFormatEndAtTextEnd(*this);
+}
+
+//class SwFormatChain
+
+bool SwFormatChain::operator==( const SfxPoolItem &rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return GetPrev() == static_cast<const SwFormatChain&>(rAttr).GetPrev() &&
+ GetNext() == static_cast<const SwFormatChain&>(rAttr).GetNext();
+}
+
+SwFormatChain::SwFormatChain( const SwFormatChain &rCpy ) :
+ SfxPoolItem( RES_CHAIN )
+{
+ SetPrev( rCpy.GetPrev() );
+ SetNext( rCpy.GetNext() );
+}
+
+SwFormatChain* SwFormatChain::Clone( SfxItemPool* ) const
+{
+ SwFormatChain *pRet = new SwFormatChain;
+ pRet->SetPrev( GetPrev() );
+ pRet->SetNext( GetNext() );
+ return pRet;
+}
+
+void SwFormatChain::SetPrev( SwFlyFrameFormat *pFormat )
+{
+ if ( pFormat )
+ pFormat->Add( &m_aPrev );
+ else
+ m_aPrev.EndListeningAll();
+}
+
+void SwFormatChain::SetNext( SwFlyFrameFormat *pFormat )
+{
+ if ( pFormat )
+ pFormat->Add( &m_aNext );
+ else
+ m_aNext.EndListeningAll();
+}
+
+bool SwFormatChain::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ OUString aRet;
+ switch ( nMemberId )
+ {
+ case MID_CHAIN_PREVNAME:
+ if ( GetPrev() )
+ aRet = GetPrev()->GetName();
+ break;
+ case MID_CHAIN_NEXTNAME:
+ if ( GetNext() )
+ aRet = GetNext()->GetName();
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ rVal <<= aRet;
+ return bRet;
+}
+
+SwFormatLineNumber::SwFormatLineNumber() :
+ SfxPoolItem( RES_LINENUMBER )
+{
+ m_nStartValue = 0;
+ m_bCountLines = true;
+}
+
+SwFormatLineNumber::~SwFormatLineNumber()
+{
+}
+
+bool SwFormatLineNumber::operator==( const SfxPoolItem &rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return m_nStartValue == static_cast<const SwFormatLineNumber&>(rAttr).GetStartValue() &&
+ m_bCountLines == static_cast<const SwFormatLineNumber&>(rAttr).IsCount();
+}
+
+SwFormatLineNumber* SwFormatLineNumber::Clone( SfxItemPool* ) const
+{
+ return new SwFormatLineNumber( *this );
+}
+
+bool SwFormatLineNumber::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_LINENUMBER_COUNT:
+ rVal <<= IsCount();
+ break;
+ case MID_LINENUMBER_STARTVALUE:
+ rVal <<= static_cast<sal_Int32>(GetStartValue());
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatLineNumber::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_LINENUMBER_COUNT:
+ SetCountLines( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_LINENUMBER_STARTVALUE:
+ {
+ sal_Int32 nVal = 0;
+ if(rVal >>= nVal)
+ SetStartValue( nVal );
+ else
+ bRet = false;
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+SwTextGridItem::SwTextGridItem()
+ : SfxPoolItem( RES_TEXTGRID ), m_aColor( COL_LIGHTGRAY ), m_nLines( 20 )
+ , m_nBaseHeight( 400 ), m_nRubyHeight( 200 ), m_eGridType( GRID_NONE )
+ , m_bRubyTextBelow( false ), m_bPrintGrid( true ), m_bDisplayGrid( true )
+ , m_nBaseWidth(400), m_bSnapToChars( true ), m_bSquaredMode(true)
+{
+}
+
+SwTextGridItem::~SwTextGridItem()
+{
+}
+
+bool SwTextGridItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ SwTextGridItem const& rOther(static_cast<SwTextGridItem const&>(rAttr));
+ return m_eGridType == rOther.GetGridType()
+ && m_nLines == rOther.GetLines()
+ && m_nBaseHeight == rOther.GetBaseHeight()
+ && m_nRubyHeight == rOther.GetRubyHeight()
+ && m_bRubyTextBelow == rOther.GetRubyTextBelow()
+ && m_bDisplayGrid == rOther.GetDisplayGrid()
+ && m_bPrintGrid == rOther.GetPrintGrid()
+ && m_aColor == rOther.GetColor()
+ && m_nBaseWidth == rOther.GetBaseWidth()
+ && m_bSnapToChars == rOther.GetSnapToChars()
+ && m_bSquaredMode == rOther.GetSquaredMode();
+}
+
+SwTextGridItem* SwTextGridItem::Clone( SfxItemPool* ) const
+{
+ return new SwTextGridItem( *this );
+}
+
+bool SwTextGridItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bRet = true;
+
+ switch( nMemberId & ~CONVERT_TWIPS )
+ {
+ case MID_GRID_COLOR:
+ rVal <<= GetColor();
+ break;
+ case MID_GRID_LINES:
+ rVal <<= GetLines();
+ break;
+ case MID_GRID_RUBY_BELOW:
+ rVal <<= m_bRubyTextBelow;
+ break;
+ case MID_GRID_PRINT:
+ rVal <<= m_bPrintGrid;
+ break;
+ case MID_GRID_DISPLAY:
+ rVal <<= m_bDisplayGrid;
+ break;
+ case MID_GRID_BASEHEIGHT:
+ OSL_ENSURE( (nMemberId & CONVERT_TWIPS) != 0,
+ "This value needs TWIPS-MM100 conversion" );
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(m_nBaseHeight));
+ break;
+ case MID_GRID_BASEWIDTH:
+ OSL_ENSURE( (nMemberId & CONVERT_TWIPS) != 0,
+ "This value needs TWIPS-MM100 conversion" );
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(m_nBaseWidth));
+ break;
+ case MID_GRID_RUBYHEIGHT:
+ OSL_ENSURE( (nMemberId & CONVERT_TWIPS) != 0,
+ "This value needs TWIPS-MM100 conversion" );
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(m_nRubyHeight));
+ break;
+ case MID_GRID_TYPE:
+ switch( GetGridType() )
+ {
+ case GRID_NONE:
+ rVal <<= text::TextGridMode::NONE;
+ break;
+ case GRID_LINES_ONLY:
+ rVal <<= text::TextGridMode::LINES;
+ break;
+ case GRID_LINES_CHARS:
+ rVal <<= text::TextGridMode::LINES_AND_CHARS;
+ break;
+ default:
+ OSL_FAIL("unknown SwTextGrid value");
+ bRet = false;
+ break;
+ }
+ break;
+ case MID_GRID_SNAPTOCHARS:
+ rVal <<= m_bSnapToChars;
+ break;
+ case MID_GRID_STANDARD_MODE:
+ rVal <<= !m_bSquaredMode;
+ break;
+ default:
+ OSL_FAIL("Unknown SwTextGridItem member");
+ bRet = false;
+ break;
+ }
+
+ return bRet;
+}
+
+bool SwTextGridItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bRet = true;
+ switch( nMemberId & ~CONVERT_TWIPS )
+ {
+ case MID_GRID_COLOR:
+ {
+ Color nTmp;
+ bRet = (rVal >>= nTmp);
+ if( bRet )
+ SetColor( nTmp );
+ }
+ break;
+ case MID_GRID_LINES:
+ {
+ sal_Int16 nTmp = 0;
+ bRet = (rVal >>= nTmp);
+ if( bRet && (nTmp >= 0) )
+ SetLines( o3tl::narrowing<sal_uInt16>(nTmp) );
+ else
+ bRet = false;
+ }
+ break;
+ case MID_GRID_RUBY_BELOW:
+ SetRubyTextBelow( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_GRID_PRINT:
+ SetPrintGrid( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_GRID_DISPLAY:
+ SetDisplayGrid( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_GRID_BASEHEIGHT:
+ case MID_GRID_BASEWIDTH:
+ case MID_GRID_RUBYHEIGHT:
+ {
+ OSL_ENSURE( (nMemberId & CONVERT_TWIPS) != 0,
+ "This value needs TWIPS-MM100 conversion" );
+ sal_Int32 nTmp = 0;
+ bRet = (rVal >>= nTmp);
+ nTmp = o3tl::toTwips(nTmp, o3tl::Length::mm100);
+ if( bRet && (nTmp >= 0) && ( nTmp <= SAL_MAX_UINT16) )
+ {
+ // rhbz#1043551 round up to 5pt -- 0 causes divide-by-zero
+ // in layout; 1pt ties the painting code up in knots for
+ // minutes with bazillion lines...
+#define MIN_TEXTGRID_SIZE 100
+ if( (nMemberId & ~CONVERT_TWIPS) == MID_GRID_BASEHEIGHT )
+ {
+ nTmp = std::max<sal_Int32>(nTmp, MIN_TEXTGRID_SIZE);
+ SetBaseHeight( o3tl::narrowing<sal_uInt16>(nTmp) );
+ }
+ else if( (nMemberId & ~CONVERT_TWIPS) == MID_GRID_BASEWIDTH )
+ {
+ nTmp = std::max<sal_Int32>(nTmp, MIN_TEXTGRID_SIZE);
+ SetBaseWidth( o3tl::narrowing<sal_uInt16>(nTmp) );
+ }
+ else
+ SetRubyHeight( o3tl::narrowing<sal_uInt16>(nTmp) );
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_GRID_TYPE:
+ {
+ sal_Int16 nTmp = 0;
+ bRet = (rVal >>= nTmp);
+ if( bRet )
+ {
+ switch( nTmp )
+ {
+ case text::TextGridMode::NONE:
+ SetGridType( GRID_NONE );
+ break;
+ case text::TextGridMode::LINES:
+ SetGridType( GRID_LINES_ONLY );
+ break;
+ case text::TextGridMode::LINES_AND_CHARS:
+ SetGridType( GRID_LINES_CHARS );
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ }
+ break;
+ }
+ case MID_GRID_SNAPTOCHARS:
+ SetSnapToChars( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_GRID_STANDARD_MODE:
+ {
+ bool bStandard = *o3tl::doAccess<bool>(rVal);
+ SetSquaredMode( !bStandard );
+ break;
+ }
+ default:
+ OSL_FAIL("Unknown SwTextGridItem member");
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+void SwTextGridItem::SwitchPaperMode(bool bNew)
+{
+ if (bNew == m_bSquaredMode)
+ {
+ //same paper mode, not switch
+ return;
+ }
+
+ // use default value when grid is disable
+ if (m_eGridType == GRID_NONE)
+ {
+ m_bSquaredMode = bNew;
+ Init();
+ return;
+ }
+
+ if (m_bSquaredMode)
+ {
+ //switch from "squared mode" to "standard mode"
+ m_nBaseWidth = m_nBaseHeight;
+ m_nBaseHeight = m_nBaseHeight + m_nRubyHeight;
+ m_nRubyHeight = 0;
+ }
+ else
+ {
+ //switch from "standard mode" to "squared mode"
+ m_nRubyHeight = m_nBaseHeight/3;
+ m_nBaseHeight = m_nBaseHeight - m_nRubyHeight;
+ m_nBaseWidth = m_nBaseHeight;
+ }
+ m_bSquaredMode = !m_bSquaredMode;
+}
+
+void SwTextGridItem::Init()
+{
+ if (m_bSquaredMode)
+ {
+ m_nLines = 20;
+ m_nBaseHeight = 400;
+ m_nRubyHeight = 200;
+ m_eGridType = GRID_NONE;
+ m_bRubyTextBelow = false;
+ m_bPrintGrid = true;
+ m_bDisplayGrid = true;
+ m_bSnapToChars = true;
+ m_nBaseWidth = 400;
+ }
+ else
+ {
+ m_nLines = 44;
+ m_nBaseHeight = 312;
+ m_nRubyHeight = 0;
+ m_eGridType = GRID_NONE;
+ m_bRubyTextBelow = false;
+ m_bPrintGrid = true;
+ m_bDisplayGrid = true;
+ m_nBaseWidth = 210;
+ m_bSnapToChars = true;
+ }
+}
+
+SwHeaderAndFooterEatSpacingItem* SwHeaderAndFooterEatSpacingItem::Clone( SfxItemPool* ) const
+{
+ return new SwHeaderAndFooterEatSpacingItem( Which(), GetValue() );
+}
+
+SwFrameFormat::SwFrameFormat(
+ SwAttrPool& rPool,
+ const char* pFormatNm,
+ SwFrameFormat *pDrvdFrame,
+ sal_uInt16 nFormatWhich,
+ const WhichRangesContainer& pWhichRange)
+: SwFormat(rPool, pFormatNm, pWhichRange, pDrvdFrame, nFormatWhich),
+ m_ffList(nullptr)
+{
+}
+
+SwFrameFormat::SwFrameFormat(
+ SwAttrPool& rPool,
+ const OUString &rFormatNm,
+ SwFrameFormat *pDrvdFrame,
+ sal_uInt16 nFormatWhich,
+ const WhichRangesContainer& pWhichRange)
+: SwFormat(rPool, rFormatNm, pWhichRange, pDrvdFrame, nFormatWhich),
+ m_ffList(nullptr)
+{
+}
+
+SwFrameFormat::~SwFrameFormat()
+{
+ if( !GetDoc()->IsInDtor())
+ {
+ const SwFormatAnchor& rAnchor = GetAnchor();
+ if (rAnchor.GetContentAnchor() != nullptr)
+ {
+ rAnchor.GetContentAnchor()->nNode.GetNode().RemoveAnchoredFly(this);
+ }
+ }
+
+ // Check if there any textboxes attached to this format.
+ if( nullptr == m_pOtherTextBoxFormats )
+ return;
+
+ // This is a fly-frame-format just delete this
+ // textbox entry from the textbox collection.
+ // Note: Do not delete it from the doc, as that
+ // is already in progress.
+ if (Which() == RES_FLYFRMFMT)
+ m_pOtherTextBoxFormats->DelTextBox(this);
+
+ // This is a draw-frame-format what belongs to
+ // a shape with textbox(es). Delete all of them.
+ if (Which() == RES_DRAWFRMFMT)
+ m_pOtherTextBoxFormats->ClearAll();
+
+ // Release the pointer.
+ m_pOtherTextBoxFormats.reset();
+}
+
+void SwFrameFormat::SetName( const OUString& rNewName, bool bBroadcast )
+{
+ if (m_ffList != nullptr) {
+ SwFrameFormats::iterator it = m_ffList->find( this );
+ assert( m_ffList->end() != it );
+ SAL_INFO_IF(m_aFormatName == rNewName, "sw.core", "SwFrmFmt not really renamed, as both names are equal");
+
+ const SwStringMsgPoolItem aOld( RES_NAME_CHANGED, m_aFormatName );
+ // As it's a non-unique list, rename should never fail!
+ bool const renamed =
+ m_ffList->m_PosIndex.modify( it,
+ change_name( rNewName ), change_name( m_aFormatName ) );
+ assert(renamed);
+ (void)renamed; // unused in NDEBUG
+ if (bBroadcast) {
+ const SwStringMsgPoolItem aNew( RES_NAME_CHANGED, rNewName );
+ GetNotifier().Broadcast(sw::LegacyModifyHint( &aOld, &aNew ));
+ }
+ }
+ else
+ SwFormat::SetName( rNewName, bBroadcast );
+}
+
+bool SwFrameFormat::supportsFullDrawingLayerFillAttributeSet() const
+{
+ return true;
+}
+
+void SwFrameFormat::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const sal_uInt16 nNewWhich = pLegacy->m_pNew ? pLegacy->m_pNew->Which() : 0;
+ const SwAttrSetChg* pNewAttrSetChg = nullptr;
+ const SwFormatHeader* pH = nullptr;
+ const SwFormatFooter* pF = nullptr;
+ const SwPosition* pNewAnchorPosition = nullptr;
+ switch(nNewWhich)
+ {
+ case RES_ATTRSET_CHG:
+ {
+ pNewAttrSetChg = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ pH = pNewAttrSetChg->GetChgSet()->GetItem(RES_HEADER, false);
+ pF = pNewAttrSetChg->GetChgSet()->GetItem(RES_FOOTER, false);
+
+ // reset fill information
+ if(maFillAttributes && supportsFullDrawingLayerFillAttributeSet())
+ {
+ SfxItemIter aIter(*pNewAttrSetChg->GetChgSet());
+ for(const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if(!IsInvalidItem(pItem) && pItem->Which() >= XATTR_FILL_FIRST && pItem->Which() <= XATTR_FILL_LAST)
+ {
+ maFillAttributes.reset();
+ break;
+ }
+ }
+ }
+ const SwFormatAnchor* pAnchor = pNewAttrSetChg->GetChgSet()->GetItem(RES_ANCHOR, false);
+ if(pAnchor)
+ {
+ pNewAnchorPosition = pAnchor->GetContentAnchor();
+ assert(pNewAnchorPosition == nullptr || // style's set must not contain position!
+ pNewAttrSetChg->GetTheChgdSet() == &m_aSet);
+ }
+ break;
+ }
+ case RES_FMT_CHG:
+ {
+ // reset fill information on format change (e.g. style changed)
+ if(maFillAttributes && supportsFullDrawingLayerFillAttributeSet())
+ maFillAttributes.reset();
+ break;
+ }
+ case RES_HEADER:
+ pH = static_cast<const SwFormatHeader*>(pLegacy->m_pNew);
+ break;
+ case RES_FOOTER:
+ pF = static_cast<const SwFormatFooter*>(pLegacy->m_pNew);
+ break;
+ case RES_ANCHOR:
+ pNewAnchorPosition = static_cast<const SwFormatAnchor*>(pLegacy->m_pNew)->GetContentAnchor();
+ break;
+ }
+ const sal_uInt16 nOldWhich = pLegacy->m_pOld ? pLegacy->m_pOld->Which() : 0;
+ const SwPosition* pOldAnchorPosition = nullptr;
+ switch(nOldWhich)
+ {
+ case RES_ATTRSET_CHG:
+ {
+ const SwAttrSetChg* pOldAttrSetChg = nullptr;
+ pOldAttrSetChg = static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ const SwFormatAnchor* pAnchor = pOldAttrSetChg->GetChgSet()->GetItem(RES_ANCHOR, false);
+ if(pAnchor)
+ {
+ pOldAnchorPosition = pAnchor->GetContentAnchor();
+ assert(pOldAnchorPosition == nullptr || // style's set must not contain position!
+ pOldAttrSetChg->GetTheChgdSet() == &m_aSet);
+ }
+ break;
+ }
+ case RES_ANCHOR:
+ pOldAnchorPosition = static_cast<const SwFormatAnchor*>(pLegacy->m_pOld)->GetContentAnchor();
+ break;
+ case RES_REMOVE_UNO_OBJECT:
+ SetXObject(uno::Reference<uno::XInterface>(nullptr));
+ break;
+ }
+
+ assert(nOldWhich == nNewWhich || !nOldWhich || !nNewWhich);
+ if(pH && pH->IsActive() && !pH->GetHeaderFormat())
+ { //If he doesn't have one, I'll add one
+ SwFrameFormat* pFormat = GetDoc()->getIDocumentLayoutAccess().MakeLayoutFormat(RndStdIds::HEADER, nullptr);
+ const_cast<SwFormatHeader*>(pH)->RegisterToFormat(*pFormat);
+ }
+ if(pF && pF->IsActive() && !pF->GetFooterFormat())
+ { //If he doesn't have one, I'll add one
+ SwFrameFormat* pFormat = GetDoc()->getIDocumentLayoutAccess().MakeLayoutFormat(RndStdIds::FOOTER, nullptr);
+ const_cast<SwFormatFooter*>(pF)->RegisterToFormat(*pFormat);
+ }
+ SwFormat::SwClientNotify(rMod, rHint);
+ if(pOldAnchorPosition != nullptr && (pNewAnchorPosition == nullptr || pOldAnchorPosition->nNode.GetIndex() != pNewAnchorPosition->nNode.GetIndex()))
+ pOldAnchorPosition->nNode.GetNode().RemoveAnchoredFly(this);
+ if(pNewAnchorPosition != nullptr && (pOldAnchorPosition == nullptr || pOldAnchorPosition->nNode.GetIndex() != pNewAnchorPosition->nNode.GetIndex()))
+ pNewAnchorPosition->nNode.GetNode().AddAnchoredFly(this);
+}
+
+void SwFrameFormat::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add( this );
+}
+
+/// Delete all Frames that are registered in aDepend.
+void SwFrameFormat::DelFrames()
+{
+ SwIterator<SwFrame,SwFormat> aIter( *this );
+ SwFrame * pLast = aIter.First();
+ if( pLast )
+ do {
+ pLast->Cut();
+ SwFrame::DestroyFrame(pLast);
+ } while( nullptr != ( pLast = aIter.Next() ));
+}
+
+void SwFrameFormat::MakeFrames()
+{
+ assert(false); // unimplemented in base class
+}
+
+SwRect SwFrameFormat::FindLayoutRect( const bool bPrtArea, const Point* pPoint ) const
+{
+ SwRect aRet;
+ SwFrame *pFrame = nullptr;
+ if( auto pSectionFormat = dynamic_cast<const SwSectionFormat*>( this ))
+ {
+ // get the Frame using Node2Layout
+ const SwSectionNode* pSectNd = pSectionFormat->GetSectionNode();
+ if( pSectNd )
+ {
+ SwNode2Layout aTmp( *pSectNd, pSectNd->GetIndex() - 1 );
+ pFrame = aTmp.NextFrame();
+
+ if( pFrame && !pFrame->KnowsFormat(*this) )
+ {
+ // the Section doesn't have his own Frame, so if someone
+ // needs the real size, we have to implement this by requesting
+ // the matching Frame from the end.
+ // PROBLEM: what happens if SectionFrames overlaps multiple
+ // pages?
+ if( bPrtArea )
+ aRet = pFrame->getFramePrintArea();
+ else
+ {
+ aRet = pFrame->getFrameArea();
+ aRet.Pos().AdjustY( -1 );
+ }
+ pFrame = nullptr; // the rect is finished by now
+ }
+ }
+ }
+ else
+ {
+ const SwFrameType nFrameType = RES_FLYFRMFMT == Which() ? SwFrameType::Fly : FRM_ALL;
+ std::pair<Point, bool> tmp;
+ if (pPoint)
+ {
+ tmp.first = *pPoint;
+ tmp.second = false;
+ }
+ pFrame = ::GetFrameOfModify(nullptr, *this, nFrameType, nullptr, pPoint ? &tmp : nullptr);
+ }
+
+ if( pFrame )
+ {
+ if( bPrtArea )
+ aRet = pFrame->getFramePrintArea();
+ else
+ aRet = pFrame->getFrameArea();
+ }
+ return aRet;
+}
+
+SdrObject* SwFrameFormat::FindRealSdrObject()
+{
+ if( RES_FLYFRMFMT == Which() )
+ {
+ Point aNullPt;
+ std::pair<Point, bool> const tmp(aNullPt, false);
+ SwFlyFrame* pFly = static_cast<SwFlyFrame*>(::GetFrameOfModify( nullptr, *this, SwFrameType::Fly,
+ nullptr, &tmp));
+ return pFly ? pFly->GetVirtDrawObj() : nullptr;
+ }
+ return FindSdrObject();
+}
+
+bool SwFrameFormat::IsLowerOf( const SwFrameFormat& rFormat ) const
+{
+ //Also linking from inside to outside or from outside to inside is not
+ //allowed.
+ SwFlyFrame *pSFly = SwIterator<SwFlyFrame,SwFormat>(*this).First();
+ if( pSFly )
+ {
+ SwFlyFrame *pAskFly = SwIterator<SwFlyFrame,SwFormat>(rFormat).First();
+ if( pAskFly )
+ return pSFly->IsLowerOf( pAskFly );
+ }
+
+ // let's try it using the node positions
+ const SwFormatAnchor* pAnchor = &rFormat.GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) && pAnchor->GetContentAnchor())
+ {
+ const SwFrameFormats& rFormats = *GetDoc()->GetSpzFrameFormats();
+ const SwNode* pFlyNd = pAnchor->GetContentAnchor()->nNode.GetNode().
+ FindFlyStartNode();
+ while( pFlyNd )
+ {
+ // then we walk up using the anchor
+ size_t n;
+ for( n = 0; n < rFormats.size(); ++n )
+ {
+ const SwFrameFormat* pFormat = rFormats[ n ];
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if( pIdx && pFlyNd == &pIdx->GetNode() )
+ {
+ if( pFormat == this )
+ return true;
+
+ pAnchor = &pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE == pAnchor->GetAnchorId()) ||
+ !pAnchor->GetContentAnchor() )
+ {
+ return false;
+ }
+
+ pFlyNd = pAnchor->GetContentAnchor()->nNode.GetNode().
+ FindFlyStartNode();
+ break;
+ }
+ }
+ if( n >= rFormats.size() )
+ {
+ OSL_ENSURE( false, "Fly section but no format found" );
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+// #i31698#
+SwFrameFormat::tLayoutDir SwFrameFormat::GetLayoutDir() const
+{
+ return SwFrameFormat::HORI_L2R;
+}
+
+void SwFrameFormat::SetLayoutDir( const SwFrameFormat::tLayoutDir )
+{
+ // empty body, because default implementation does nothing
+}
+
+// #i28749#
+sal_Int16 SwFrameFormat::GetPositionLayoutDir() const
+{
+ return text::PositionLayoutDir::PositionInLayoutDirOfAnchor;
+}
+void SwFrameFormat::SetPositionLayoutDir( const sal_Int16 )
+{
+ // empty body, because default implementation does nothing
+}
+
+OUString SwFrameFormat::GetDescription() const
+{
+ return SwResId(STR_FRAME);
+}
+
+void SwFrameFormat::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFrameFormat"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("whichId"), "%d", Which());
+
+ const char* pWhich = nullptr;
+ switch (Which())
+ {
+ case RES_FLYFRMFMT:
+ pWhich = "fly frame format";
+ break;
+ case RES_DRAWFRMFMT:
+ pWhich = "draw frame format";
+ break;
+ }
+ if (pWhich)
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("which"), BAD_CAST(pWhich));
+
+ if (m_pOtherTextBoxFormats)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("OtherTextBoxFormat"), "%p", m_pOtherTextBoxFormats.get());
+ }
+
+ GetAttrSet().dumpAsXml(pWriter);
+
+ if (const SdrObject* pSdrObject = FindSdrObject())
+ pSdrObject->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwFrameFormats::dumpAsXml(xmlTextWriterPtr pWriter, const char* pName) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST(pName));
+ for (const SwFrameFormat *pFormat : m_PosIndex)
+ pFormat->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+SwFlyFrameFormat::SwFlyFrameFormat( SwAttrPool& rPool, const OUString &rFormatNm, SwFrameFormat *pDrvdFrame )
+ : SwFrameFormat( rPool, rFormatNm, pDrvdFrame, RES_FLYFRMFMT )
+{}
+
+SwFlyFrameFormat::~SwFlyFrameFormat()
+{
+ SwIterator<SwFlyFrame,SwFormat> aIter( *this );
+ SwFlyFrame * pLast = aIter.First();
+ if( pLast )
+ do
+ {
+ SwFrame::DestroyFrame(pLast);
+ } while( nullptr != ( pLast = aIter.Next() ));
+
+}
+
+SwFlyDrawContact* SwFlyFrameFormat::GetOrCreateContact()
+{
+ if(!m_pContact)
+ {
+ SwDrawModel* pDrawModel(GetDoc()->getIDocumentDrawModelAccess().GetOrCreateDrawModel());
+ m_pContact.reset(new SwFlyDrawContact(this, *pDrawModel));
+ }
+
+ return m_pContact.get();
+}
+
+/// Creates the Frames if the format describes a paragraph-bound frame.
+/// MA: 1994-02-14: creates the Frames also for frames anchored at page.
+void SwFlyFrameFormat::MakeFrames()
+{
+ // is there a layout?
+ if( !GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell() )
+ return;
+
+ sw::BroadcastingModify *pModify = nullptr;
+ // OD 24.07.2003 #111032# - create local copy of anchor attribute for possible changes.
+ SwFormatAnchor aAnchorAttr( GetAnchor() );
+ switch( aAnchorAttr.GetAnchorId() )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ if( aAnchorAttr.GetContentAnchor() )
+ {
+ pModify = aAnchorAttr.GetContentAnchor()->nNode.GetNode().GetContentNode();
+ }
+ break;
+
+ case RndStdIds::FLY_AT_FLY:
+ if( aAnchorAttr.GetContentAnchor() )
+ {
+ //First search in the content because this is O(1)
+ //This can go wrong for linked frames because in this case it's
+ //possible, that no Frame exists for this content.
+ //In such a situation we also need to search from StartNode to
+ //FrameFormat.
+ SwNodeIndex aIdx( aAnchorAttr.GetContentAnchor()->nNode );
+ SwContentNode *pCNd = GetDoc()->GetNodes().GoNext( &aIdx );
+ // #i105535#
+ if ( pCNd == nullptr )
+ {
+ pCNd = aAnchorAttr.GetContentAnchor()->nNode.GetNode().GetContentNode();
+ }
+ if ( pCNd )
+ {
+ if (SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*pCNd).First())
+ {
+ pModify = pCNd;
+ }
+ }
+ // #i105535#
+ if ( pModify == nullptr )
+ {
+ const SwNodeIndex &rIdx = aAnchorAttr.GetContentAnchor()->nNode;
+ SwFrameFormats& rFormats = *GetDoc()->GetSpzFrameFormats();
+ for( size_t i = 0; i < rFormats.size(); ++i )
+ {
+ SwFrameFormat* pFlyFormat = rFormats[i];
+ if( pFlyFormat->GetContent().GetContentIdx() &&
+ rIdx == *pFlyFormat->GetContent().GetContentIdx() )
+ {
+ pModify = pFlyFormat;
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ sal_uInt16 nPgNum = aAnchorAttr.GetPageNum();
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->Lower());
+ if( nPgNum == 0 && aAnchorAttr.GetContentAnchor() )
+ {
+ SwContentNode *pCNd = aAnchorAttr.GetContentAnchor()->nNode.GetNode().GetContentNode();
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pCNd);
+ for ( SwFrame* pFrame = aIter.First(); pFrame != nullptr; pFrame = aIter.Next() )
+ {
+ pPage = pFrame->FindPageFrame();
+ if( pPage )
+ {
+ nPgNum = pPage->GetPhyPageNum();
+ aAnchorAttr.SetPageNum( nPgNum );
+ aAnchorAttr.SetAnchor( nullptr );
+ SetFormatAttr( aAnchorAttr );
+ break;
+ }
+ }
+ }
+ while ( pPage )
+ {
+ if ( pPage->GetPhyPageNum() == nPgNum )
+ {
+ // #i50432# - adjust synopsis of <PlaceFly(..)>
+ pPage->PlaceFly( nullptr, this );
+ break;
+ }
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if( !pModify )
+ return;
+
+ SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(*pModify);
+ for( SwFrame *pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ bool bAdd = !pFrame->IsContentFrame() ||
+ !static_cast<SwContentFrame*>(pFrame)->IsFollow();
+
+ if ( RndStdIds::FLY_AT_FLY == aAnchorAttr.GetAnchorId() && !pFrame->IsFlyFrame() )
+ {
+ SwFrame* pFlyFrame = pFrame->FindFlyFrame();
+ if ( pFlyFrame )
+ {
+ pFrame = pFlyFrame;
+ }
+ else
+ {
+ aAnchorAttr.SetType( RndStdIds::FLY_AT_PARA );
+ SetFormatAttr( aAnchorAttr );
+ MakeFrames();
+ return;
+ }
+ }
+
+ if (bAdd)
+ {
+ switch (aAnchorAttr.GetAnchorId())
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ assert(pFrame->IsTextFrame());
+ bAdd = IsAnchoredObjShown(*static_cast<SwTextFrame*>(pFrame), aAnchorAttr);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (bAdd && pFrame->GetDrawObjs())
+ {
+ // #i28701# - new type <SwSortedObjs>
+ SwSortedObjs &rObjs = *pFrame->GetDrawObjs();
+ for(SwAnchoredObject* pObj : rObjs)
+ {
+ // #i28701# - consider changed type of
+ // <SwSortedObjs> entries.
+ if( pObj->DynCastFlyFrame() != nullptr &&
+ (&pObj->GetFrameFormat()) == this )
+ {
+ bAdd = false;
+ break;
+ }
+ }
+ }
+
+ if( bAdd )
+ {
+ SwFlyFrame *pFly = nullptr; // avoid warnings
+ switch( aAnchorAttr.GetAnchorId() )
+ {
+ case RndStdIds::FLY_AT_FLY:
+ pFly = new SwFlyLayFrame( this, pFrame, pFrame );
+ break;
+
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ pFly = new SwFlyAtContentFrame( this, pFrame, pFrame );
+ break;
+
+ case RndStdIds::FLY_AS_CHAR:
+ pFly = new SwFlyInContentFrame( this, pFrame, pFrame );
+ break;
+
+ default:
+ assert(false && "New anchor type" );
+ }
+ pFrame->AppendFly( pFly );
+ pFly->GetFormat()->SetObjTitle(GetObjTitle());
+ pFly->GetFormat()->SetObjDescription(GetObjDescription());
+ SwPageFrame *pPage = pFly->FindPageFrame();
+ if( pPage )
+ ::RegistFlys( pPage, pFly );
+ }
+ }
+}
+
+SwFlyFrame* SwFlyFrameFormat::GetFrame( const Point* pPoint ) const
+{
+ std::pair<Point, bool> tmp;
+ if (pPoint)
+ {
+ tmp.first = *pPoint;
+ tmp.second = false;
+ }
+ return static_cast<SwFlyFrame*>(::GetFrameOfModify( nullptr, *this, SwFrameType::Fly,
+ nullptr, &tmp));
+}
+
+SwAnchoredObject* SwFlyFrameFormat::GetAnchoredObj() const
+{
+ SwFlyFrame* pFlyFrame( GetFrame() );
+ if ( pFlyFrame )
+ {
+ return pFlyFrame;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+bool SwFlyFrameFormat::GetInfo( SfxPoolItem& rInfo ) const
+{
+ bool bRet = true;
+ switch( rInfo.Which() )
+ {
+ case RES_CONTENT_VISIBLE:
+ {
+ static_cast<SwPtrMsgPoolItem&>(rInfo).pObject = SwIterator<SwFrame,SwFormat>( *this ).First();
+ }
+ bRet = false;
+ break;
+
+ default:
+ bRet = SwFrameFormat::GetInfo( rInfo );
+ break;
+ }
+ return bRet;
+}
+
+// #i73249#
+void SwFlyFrameFormat::SetObjTitle( const OUString& rTitle, bool bBroadcast )
+{
+ SdrObject* pMasterObject = FindSdrObject();
+ OSL_ENSURE( pMasterObject, "<SwFlyFrameFormat::SetObjTitle(..)> - missing <SdrObject> instance" );
+ msTitle = rTitle;
+ if ( !pMasterObject )
+ {
+ return;
+ }
+
+ const SwStringMsgPoolItem aOld(RES_TITLE_CHANGED, pMasterObject->GetTitle());
+ pMasterObject->SetTitle(rTitle);
+ if(bBroadcast)
+ {
+ const SwStringMsgPoolItem aNew(RES_TITLE_CHANGED, rTitle);
+ GetNotifier().Broadcast(sw::LegacyModifyHint(&aOld, &aNew));
+ }
+}
+
+OUString SwFlyFrameFormat::GetObjTitle() const
+{
+ const SdrObject* pMasterObject = FindSdrObject();
+ OSL_ENSURE( pMasterObject, "<SwFlyFrameFormat::GetObjTitle(..)> - missing <SdrObject> instance" );
+ if ( !pMasterObject )
+ {
+ return msTitle;
+ }
+ if (!pMasterObject->GetTitle().isEmpty())
+ return pMasterObject->GetTitle();
+ else
+ return msTitle;
+}
+
+void SwFlyFrameFormat::SetObjTooltip(const OUString& rTooltip)
+{
+ msTooltip = rTooltip;
+}
+
+const OUString & SwFlyFrameFormat::GetObjTooltip() const
+{
+ return msTooltip;
+}
+
+void SwFlyFrameFormat::SetObjDescription( const OUString& rDescription, bool bBroadcast )
+{
+ SdrObject* pMasterObject = FindSdrObject();
+ OSL_ENSURE( pMasterObject, "<SwFlyFrameFormat::SetDescription(..)> - missing <SdrObject> instance" );
+ msDesc = rDescription;
+ if ( !pMasterObject )
+ {
+ return;
+ }
+
+ const SwStringMsgPoolItem aOld( RES_DESCRIPTION_CHANGED, pMasterObject->GetDescription() );
+ pMasterObject->SetDescription( rDescription );
+ if(bBroadcast)
+ {
+ const SwStringMsgPoolItem aNew( RES_DESCRIPTION_CHANGED, rDescription );
+ GetNotifier().Broadcast(sw::LegacyModifyHint(&aOld, &aNew));
+ }
+}
+
+OUString SwFlyFrameFormat::GetObjDescription() const
+{
+ const SdrObject* pMasterObject = FindSdrObject();
+ OSL_ENSURE( pMasterObject, "<SwFlyFrameFormat::GetDescription(..)> - missing <SdrObject> instance" );
+ if ( !pMasterObject )
+ {
+ return msDesc;
+ }
+ if (!pMasterObject->GetDescription().isEmpty())
+ return pMasterObject->GetDescription();
+ else
+ return msDesc;
+}
+
+/** SwFlyFrameFormat::IsBackgroundTransparent - for #99657#
+
+ OD 22.08.2002 - overriding virtual method and its default implementation,
+ because format of fly frame provides transparent backgrounds.
+ Method determines, if background of fly frame is transparent.
+
+ @return true, if background color is transparent, but not "no fill"
+ or the transparency of an existing background graphic is set.
+*/
+bool SwFlyFrameFormat::IsBackgroundTransparent() const
+{
+ if (supportsFullDrawingLayerFillAttributeSet() && getSdrAllFillAttributesHelper())
+ {
+ return getSdrAllFillAttributesHelper()->isTransparent();
+ }
+
+ // NOTE: If background color is "no fill"/"auto fill" (COL_TRANSPARENT)
+ // and there is no background graphic, it "inherites" the background
+ // from its anchor.
+ std::unique_ptr<SvxBrushItem> aBackground(makeBackgroundBrushItem());
+ if ( aBackground->GetColor().IsTransparent() &&
+ aBackground->GetColor() != COL_TRANSPARENT
+ )
+ {
+ return true;
+ }
+ else
+ {
+ const GraphicObject *pTmpGrf = aBackground->GetGraphicObject();
+ if ( pTmpGrf &&
+ pTmpGrf->GetAttr().IsTransparent()
+ )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/** SwFlyFrameFormat::IsBackgroundBrushInherited - for #103898#
+
+ OD 08.10.2002 - method to determine, if the brush for drawing the
+ background is "inherited" from its parent/grandparent.
+ This is the case, if no background graphic is set and the background
+ color is "no fill"/"auto fill"
+ NOTE: condition is "copied" from method <SwFrame::GetBackgroundBrush(..).
+
+ @return true, if background brush is "inherited" from parent/grandparent
+*/
+bool SwFlyFrameFormat::IsBackgroundBrushInherited() const
+{
+ if (supportsFullDrawingLayerFillAttributeSet() && getSdrAllFillAttributesHelper())
+ {
+ return !getSdrAllFillAttributesHelper()->isUsed();
+ }
+ else
+ {
+ std::unique_ptr<SvxBrushItem> aBackground(makeBackgroundBrushItem());
+ if ( (aBackground->GetColor() == COL_TRANSPARENT) &&
+ !(aBackground->GetGraphicObject()) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+SwHandleAnchorNodeChg::SwHandleAnchorNodeChg( SwFlyFrameFormat& _rFlyFrameFormat,
+ const SwFormatAnchor& _rNewAnchorFormat,
+ SwFlyFrame const * _pKeepThisFlyFrame )
+ : mrFlyFrameFormat( _rFlyFrameFormat ),
+ mbAnchorNodeChanged( false ),
+ mpWrtShell(nullptr)
+{
+ const SwFormatAnchor& aOldAnchorFormat(_rFlyFrameFormat.GetAnchor());
+ const RndStdIds nNewAnchorType( _rNewAnchorFormat.GetAnchorId() );
+ if ( ((nNewAnchorType == RndStdIds::FLY_AT_PARA) ||
+ (nNewAnchorType == RndStdIds::FLY_AT_CHAR)) &&
+ _rNewAnchorFormat.GetContentAnchor() &&
+ _rNewAnchorFormat.GetContentAnchor()->nNode.GetNode().GetContentNode() )
+ {
+ if ( aOldAnchorFormat.GetAnchorId() == nNewAnchorType &&
+ aOldAnchorFormat.GetContentAnchor() &&
+ aOldAnchorFormat.GetContentAnchor()->nNode.GetNode().GetContentNode() &&
+ aOldAnchorFormat.GetContentAnchor()->nNode !=
+ _rNewAnchorFormat.GetContentAnchor()->nNode )
+ {
+ // determine 'old' number of anchor frames
+ sal_uInt32 nOldNumOfAnchFrame( 0 );
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aOldIter(
+ *(aOldAnchorFormat.GetContentAnchor()->nNode.GetNode().GetContentNode()) );
+ for( SwFrame* pOld = aOldIter.First(); pOld; pOld = aOldIter.Next() )
+ {
+ ++nOldNumOfAnchFrame;
+ }
+ // determine 'new' number of anchor frames
+ sal_uInt32 nNewNumOfAnchFrame( 0 );
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aNewIter(
+ *(_rNewAnchorFormat.GetContentAnchor()->nNode.GetNode().GetContentNode()) );
+ for( SwFrame* pNew = aNewIter.First(); pNew; pNew = aNewIter.Next() )
+ {
+ ++nNewNumOfAnchFrame;
+ }
+ if ( nOldNumOfAnchFrame != nNewNumOfAnchFrame )
+ {
+ // delete existing fly frames except <_pKeepThisFlyFrame>
+ SwIterator<SwFrame,SwFormat> aIter( mrFlyFrameFormat );
+ SwFrame* pFrame = aIter.First();
+ if ( pFrame )
+ {
+ do {
+ if ( pFrame != _pKeepThisFlyFrame )
+ {
+ pFrame->Cut();
+ SwFrame::DestroyFrame(pFrame);
+ }
+ } while( nullptr != ( pFrame = aIter.Next() ));
+ }
+ // indicate, that re-creation of fly frames necessary
+ mbAnchorNodeChanged = true;
+ }
+ }
+ }
+
+ if (aOldAnchorFormat.GetContentAnchor()
+ && aOldAnchorFormat.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ mpCommentAnchor.reset(new SwPosition(*aOldAnchorFormat.GetContentAnchor()));
+ }
+
+ if (_pKeepThisFlyFrame)
+ {
+ SwViewShell* pViewShell = _pKeepThisFlyFrame->getRootFrame()->GetCurrShell();
+ mpWrtShell = dynamic_cast<SwWrtShell*>(pViewShell);
+ }
+}
+
+void SwHandleAnchorNodeChg::ImplDestroy()
+{
+ if ( mbAnchorNodeChanged )
+ {
+ mrFlyFrameFormat.MakeFrames();
+ }
+
+ // See if the fly frame had a comment: if so, move it to the new anchor as well.
+ if (!mpCommentAnchor)
+ {
+ return;
+ }
+
+ SwTextNode* pTextNode = mpCommentAnchor->nNode.GetNode().GetTextNode();
+ if (!pTextNode)
+ {
+ return;
+ }
+
+ const SwTextField* pField = pTextNode->GetFieldTextAttrAt(mpCommentAnchor->nContent.GetIndex());
+ if (!pField || pField->GetFormatField().GetField()->GetTyp()->Which() != SwFieldIds::Postit)
+ {
+ return;
+ }
+
+ if (!mpWrtShell)
+ {
+ return;
+ }
+
+ // Save current cursor position, so we can restore it later.
+ mpWrtShell->Push();
+
+ // Set up the source of the move: the old comment anchor.
+ {
+ SwPaM& rCursor = mpWrtShell->GetCurrentShellCursor();
+ *rCursor.GetPoint() = *mpCommentAnchor;
+ rCursor.SetMark();
+ *rCursor.GetMark() = *mpCommentAnchor;
+ ++rCursor.GetMark()->nContent;
+ }
+
+ // Set up the target of the move: the new comment anchor.
+ const SwFormatAnchor& rNewAnchorFormat = mrFlyFrameFormat.GetAnchor();
+ mpWrtShell->CreateCursor();
+ *mpWrtShell->GetCurrentShellCursor().GetPoint() = *rNewAnchorFormat.GetContentAnchor();
+
+ // Move by copying and deleting.
+ mpWrtShell->SwEditShell::Copy(*mpWrtShell);
+ mpWrtShell->DestroyCursor();
+
+ mpWrtShell->Delete(false);
+
+ mpWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
+}
+
+SwHandleAnchorNodeChg::~SwHandleAnchorNodeChg()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+namespace sw
+{
+ DrawFrameFormatHint::~DrawFrameFormatHint() {}
+ CheckDrawFrameFormatLayerHint::~CheckDrawFrameFormatLayerHint() {}
+ ContactChangedHint::~ContactChangedHint() {}
+ DrawFormatLayoutCopyHint::~DrawFormatLayoutCopyHint() {}
+ WW8AnchorConvHint::~WW8AnchorConvHint() {}
+ RestoreFlyAnchorHint::~RestoreFlyAnchorHint() {}
+ CreatePortionHint::~CreatePortionHint() {}
+ FindSdrObjectHint::~FindSdrObjectHint() {}
+ CollectTextObjectsHint::~CollectTextObjectsHint() {}
+ GetZOrderHint::~GetZOrderHint() {}
+ GetObjectConnectedHint::~GetObjectConnectedHint() {}
+}
+
+SwDrawFrameFormat::~SwDrawFrameFormat()
+{
+ CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::DYING));
+}
+
+void SwDrawFrameFormat::MakeFrames()
+{
+ CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::MAKE_FRAMES));
+}
+
+void SwDrawFrameFormat::DelFrames()
+{
+ CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::DELETE_FRAMES));
+}
+
+// #i31698#
+SwFrameFormat::tLayoutDir SwDrawFrameFormat::GetLayoutDir() const
+{
+ return meLayoutDir;
+}
+
+void SwDrawFrameFormat::SetLayoutDir( const SwFrameFormat::tLayoutDir _eLayoutDir )
+{
+ meLayoutDir = _eLayoutDir;
+}
+
+// #i28749#
+sal_Int16 SwDrawFrameFormat::GetPositionLayoutDir() const
+{
+ return mnPositionLayoutDir;
+}
+void SwDrawFrameFormat::SetPositionLayoutDir( const sal_Int16 _nPositionLayoutDir )
+{
+ switch ( _nPositionLayoutDir )
+ {
+ case text::PositionLayoutDir::PositionInHoriL2R:
+ case text::PositionLayoutDir::PositionInLayoutDirOfAnchor:
+ {
+ mnPositionLayoutDir = _nPositionLayoutDir;
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "<SwDrawFrameFormat::SetPositionLayoutDir(..)> - invalid attribute value." );
+ }
+ }
+}
+
+OUString SwDrawFrameFormat::GetDescription() const
+{
+ OUString aResult;
+ const SdrObject * pSdrObj = FindSdrObject();
+
+ if (pSdrObj)
+ {
+ if (pSdrObj != m_pSdrObjectCached)
+ {
+ m_sSdrObjectCachedComment = SdrUndoNewObj::GetComment(*pSdrObj);
+ m_pSdrObjectCached = pSdrObj;
+ }
+
+ aResult = m_sSdrObjectCachedComment;
+ }
+ else
+ aResult = SwResId(STR_GRAPHIC);
+
+ return aResult;
+}
+
+IMapObject* SwFrameFormat::GetIMapObject( const Point& rPoint,
+ const SwFlyFrame *pFly ) const
+{
+ const SwFormatURL &rURL = GetURL();
+ if( !rURL.GetMap() )
+ return nullptr;
+
+ if( !pFly )
+ {
+ pFly = SwIterator<SwFlyFrame,SwFormat>( *this ).First();
+ if( !pFly )
+ return nullptr;
+ }
+
+ //Original size for OLE and graphic is TwipSize, otherwise the size of
+ //FrameFormat of the Fly.
+ const SwFrame *pRef;
+ const SwNoTextNode *pNd = nullptr;
+ Size aOrigSz;
+ if( pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
+ {
+ pRef = pFly->Lower();
+ pNd = static_cast<const SwNoTextFrame*>(pRef)->GetNode()->GetNoTextNode();
+ aOrigSz = pNd->GetTwipSize();
+ }
+ else
+ {
+ pRef = pFly;
+ aOrigSz = pFly->GetFormat()->GetFrameSize().GetSize();
+ }
+
+ if( !aOrigSz.IsEmpty() )
+ {
+ Point aPos( rPoint );
+ Size aActSz ( pRef == pFly ? pFly->getFrameArea().SSize() : pRef->getFramePrintArea().SSize() );
+ const o3tl::Length aSrc ( o3tl::Length::twip );
+ const o3tl::Length aDest( o3tl::Length::mm100 );
+ aOrigSz = o3tl::convert( aOrigSz, aSrc, aDest );
+ aActSz = o3tl::convert( aActSz, aSrc, aDest );
+ aPos -= pRef->getFrameArea().Pos();
+ aPos -= pRef->getFramePrintArea().Pos();
+ aPos = o3tl::convert( aPos, aSrc, aDest );
+ sal_uInt32 nFlags = 0;
+ if ( pFly != pRef && pNd->IsGrfNode() )
+ {
+ const MirrorGraph nMirror = pNd->GetSwAttrSet().
+ GetMirrorGrf().GetValue();
+ if ( MirrorGraph::Both == nMirror )
+ nFlags = IMAP_MIRROR_HORZ | IMAP_MIRROR_VERT;
+ else if ( MirrorGraph::Vertical == nMirror )
+ nFlags = IMAP_MIRROR_VERT;
+ else if ( MirrorGraph::Horizontal == nMirror )
+ nFlags = IMAP_MIRROR_HORZ;
+
+ }
+ return const_cast<ImageMap*>(rURL.GetMap())->GetHitIMapObject( aOrigSz,
+ aActSz, aPos, nFlags );
+ }
+
+ return nullptr;
+}
+
+drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwFrameFormat::getSdrAllFillAttributesHelper() const
+{
+ if (supportsFullDrawingLayerFillAttributeSet())
+ {
+ // create FillAttributes on demand
+ if(!maFillAttributes)
+ {
+ const_cast< SwFrameFormat* >(this)->maFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(GetAttrSet());
+ }
+ }
+ else
+ {
+ // FALLBACKBREAKHERE assert wrong usage
+ OSL_ENSURE(false, "getSdrAllFillAttributesHelper() call only valid for RES_FLYFRMFMT and RES_FRMFMT (!)");
+ }
+
+ return maFillAttributes;
+}
+
+void SwFrameFormat::MoveTableBox(SwTableBox& rTableBox, const SwFrameFormat* pOldFormat)
+{
+ Add(&rTableBox);
+ if(!pOldFormat)
+ return;
+ const auto& rOld = pOldFormat->GetFormatAttr(RES_BOXATR_FORMAT);
+ const auto& rNew = GetFormatAttr(RES_BOXATR_FORMAT);
+ if(rOld != rNew)
+ SwClientNotify(*this, sw::LegacyModifyHint(&rOld, &rNew));
+}
+
+
+namespace sw {
+
+bool IsFlyFrameFormatInHeader(const SwFrameFormat& rFormat)
+{
+ const SwFlyFrameFormat* pFlyFrameFormat = dynamic_cast<const SwFlyFrameFormat*>(&rFormat);
+ if (!pFlyFrameFormat)
+ return false;
+ SwFlyFrame* pFlyFrame = pFlyFrameFormat->GetFrame();
+ if (!pFlyFrame) // fdo#54648: "hidden" drawing object has no layout frame
+ {
+ return false;
+ }
+ SwPageFrame* pPageFrame = pFlyFrame->FindPageFrameOfAnchor();
+ SwFrame* pHeader = pPageFrame->Lower();
+ if (pHeader->GetType() == SwFrameType::Header)
+ {
+ const SwFrame* pFrame = pFlyFrame->GetAnchorFrame();
+ while (pFrame)
+ {
+ if (pFrame == pHeader)
+ return true;
+ pFrame = pFrame->GetUpper();
+ }
+ }
+ return false;
+}
+
+void CheckAnchoredFlyConsistency(SwDoc const& rDoc)
+{
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ SwNodes const& rNodes(rDoc.GetNodes());
+ SwNodeOffset const count(rNodes.Count());
+ for (SwNodeOffset i(0); i != count; ++i)
+ {
+ SwNode const*const pNode(rNodes[i]);
+ std::vector<SwFrameFormat*> const & rFlys(pNode->GetAnchoredFlys());
+ for (const auto& rpFly : rFlys)
+ {
+ SwFormatAnchor const& rAnchor((*rpFly).GetAnchor(false));
+ assert(&rAnchor.GetContentAnchor()->nNode.GetNode() == pNode);
+ }
+ }
+ SwFrameFormats const*const pSpzFrameFormats(rDoc.GetSpzFrameFormats());
+ if (!pSpzFrameFormats)
+ return;
+
+ for (auto it = pSpzFrameFormats->begin(); it != pSpzFrameFormats->end(); ++it)
+ {
+ SwFormatAnchor const& rAnchor((**it).GetAnchor(false));
+ if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId())
+ {
+ assert(!rAnchor.GetContentAnchor()
+ // for invalid documents that lack text:anchor-page-number
+ // it may have an anchor before MakeFrames() is called
+ || (!SwIterator<SwFrame, SwFrameFormat>(**it).First()));
+ }
+ else
+ {
+ SwNode & rNode(rAnchor.GetContentAnchor()->nNode.GetNode());
+ std::vector<SwFrameFormat*> const& rFlys(rNode.GetAnchoredFlys());
+ assert(std::find(rFlys.begin(), rFlys.end(), *it) != rFlys.end());
+ switch (rAnchor.GetAnchorId())
+ {
+ case RndStdIds::FLY_AT_FLY:
+ assert(rNode.IsStartNode());
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ assert(rNode.IsTextNode() || rNode.IsTableNode());
+ break;
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ assert(rNode.IsTextNode());
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ }
+#else
+ (void) rDoc;
+#endif
+}
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/calcmove.cxx b/sw/source/core/layout/calcmove.cxx
new file mode 100644
index 000000000..184373585
--- /dev/null
+++ b/sw/source/core/layout/calcmove.cxx
@@ -0,0 +1,2215 @@
+/* -*- 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 <memory>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewopt.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <ndtxt.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <osl/diagnose.h>
+#include <svx/sdtaitm.hxx>
+
+#include <fmtfsize.hxx>
+#include <fmtanchr.hxx>
+#include <fmtclbl.hxx>
+
+#include <tabfrm.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <sectfrm.hxx>
+#include <dbg_lay.hxx>
+
+#include <sortedobjs.hxx>
+#include <layouter.hxx>
+#include <flyfrms.hxx>
+
+#include <DocumentSettingManager.hxx>
+#include <IDocumentLayoutAccess.hxx>
+
+// Move methods
+
+/// Return value tells whether the Frame should be moved.
+bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool & )
+{
+ if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
+ {
+ // Floating back a frm uses a bit of time unfortunately.
+ // The most common case is the following: The Frame wants to float to
+ // somewhere where the FixSize is the same that the Frame itself has already.
+ // In that case it's pretty easy to check if the Frame has enough space
+ // for its VarSize. If this is NOT the case, we already know that
+ // we don't need to move.
+ // The Frame checks itself whether it has enough space - respecting the fact
+ // that it could possibly split itself if needed.
+ // If, however, the FixSize differs from the Frame or Flys are involved
+ // (either in the old or the new position), checking is pointless,
+ // and we have to move the Frame just to see what happens - if there's
+ // some space available to do it, that is.
+
+ // The FixSize of the containers of Contents is always the width.
+
+ // If we moved more than one sheet back (for example jumping over empty
+ // pages), we have to move either way. Otherwise, if the Frame doesn't fit
+ // into the page, empty pages wouldn't be respected anymore.
+ sal_uInt8 nMoveAnyway = 0;
+ SwPageFrame * const pNewPage = pNewUpper->FindPageFrame();
+ SwPageFrame *pOldPage = FindPageFrame();
+
+ if ( SwFlowFrame::IsMoveBwdJump() )
+ return true;
+
+ if( IsInFootnote() && IsInSct() )
+ {
+ SwFootnoteFrame* pFootnote = FindFootnoteFrame();
+ SwSectionFrame* pMySect = pFootnote->FindSctFrame();
+ if( pMySect && pMySect->IsFootnoteLock() )
+ {
+ SwSectionFrame *pSect = pNewUpper->FindSctFrame();
+ while( pSect && pSect->IsInFootnote() )
+ pSect = pSect->GetUpper()->FindSctFrame();
+ OSL_ENSURE( pSect, "Escaping footnote" );
+ if( pSect != pMySect )
+ return false;
+ }
+ }
+ SwRectFnSet aRectFnSet(this);
+ SwRectFnSet fnRectX(pNewUpper);
+ if( std::abs( fnRectX.GetWidth(pNewUpper->getFramePrintArea()) -
+ aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) ) > 1 ) {
+ // In this case, only a WouldFit_ with test move is possible
+ nMoveAnyway = 2;
+ }
+
+ // Do *not* move backward, if <nMoveAnyway> equals 3 and no space is left in new upper.
+ nMoveAnyway |= BwdMoveNecessary( pOldPage, getFrameArea() );
+ {
+ const IDocumentSettingAccess& rIDSA = pNewPage->GetFormat()->getIDocumentSettingAccess();
+ SwTwips nSpace = 0;
+ SwRect aRect( pNewUpper->getFramePrintArea() );
+ aRect.Pos() += pNewUpper->getFrameArea().Pos();
+ const SwFrame *pPrevFrame = pNewUpper->Lower();
+ while ( pPrevFrame )
+ {
+ SwTwips nNewTop = fnRectX.GetBottom(pPrevFrame->getFrameArea());
+ // Consider lower spacing of last frame in a table cell
+ {
+ // Check if last frame is inside table and if it includes its lower spacing.
+ if ( !pPrevFrame->GetNext() && pPrevFrame->IsInTab() &&
+ rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) )
+ {
+ const SwFrame* pLastFrame = pPrevFrame;
+ // if last frame is a section, take its last content
+ if ( pPrevFrame->IsSctFrame() )
+ {
+ pLastFrame = static_cast<const SwSectionFrame*>(pPrevFrame)->FindLastContent();
+ if ( pLastFrame &&
+ pLastFrame->FindTabFrame() != pPrevFrame->FindTabFrame() )
+ {
+ pLastFrame = pLastFrame->FindTabFrame();
+ }
+ }
+
+ if ( pLastFrame )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pLastFrame );
+ const SwBorderAttrs& rAttrs = *aAccess.Get();
+ nNewTop -= rAttrs.GetULSpace().GetLower();
+ if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
+ {
+ nNewTop -= rAttrs.CalcLineSpacing();
+ }
+ }
+ }
+ }
+ fnRectX.SetTop( aRect, nNewTop );
+
+ pPrevFrame = pPrevFrame->GetNext();
+ }
+
+ nMoveAnyway |= BwdMoveNecessary( pNewPage, aRect);
+
+ //determine space left in new upper frame
+ nSpace = fnRectX.GetHeight(aRect);
+ const SwViewShell *pSh = pNewUpper->getRootFrame()->GetCurrShell();
+ if ( IsInFootnote() ||
+ (pSh && pSh->GetViewOptions()->getBrowseMode()) ||
+ pNewUpper->IsCellFrame() ||
+ ( pNewUpper->IsInSct() && ( pNewUpper->IsSctFrame() ||
+ ( pNewUpper->IsColBodyFrame() &&
+ !pNewUpper->GetUpper()->GetPrev() &&
+ !pNewUpper->GetUpper()->GetNext() ) ) ) )
+ nSpace += pNewUpper->Grow( LONG_MAX, true );
+
+ if ( nMoveAnyway < 3 )
+ {
+ if ( nSpace )
+ {
+ // Do not notify footnotes which are stuck to the paragraph:
+ // This would require extremely confusing code, taking into
+ // account the widths
+ // and Flys, that in turn influence the footnotes, ...
+
+ // WouldFit_ can only be used if the width is the same and
+ // ONLY self-anchored Flys are present.
+
+ // WouldFit_ can also be used if ONLY Flys anchored
+ // somewhere else are present.
+ // In this case, the width doesn't even matter,
+ // because we're running a TestFormat in the new upper.
+ const sal_uInt8 nBwdMoveNecessaryResult =
+ BwdMoveNecessary( pNewPage, aRect);
+ const bool bObjsInNewUpper( nBwdMoveNecessaryResult == 2 ||
+ nBwdMoveNecessaryResult == 3 );
+
+ return WouldFit_( nSpace, pNewUpper, nMoveAnyway == 2,
+ bObjsInNewUpper );
+ }
+ // It's impossible for WouldFit_ to return a usable result if
+ // we have a fresh multi-column section - so we really have to
+ // float back unless there is no space.
+ return pNewUpper->IsInSct() && pNewUpper->IsColBodyFrame() &&
+ !fnRectX.GetWidth(pNewUpper->getFramePrintArea()) &&
+ ( pNewUpper->GetUpper()->GetPrev() ||
+ pNewUpper->GetUpper()->GetNext() );
+ }
+
+ // Check for space left in new upper
+ return nSpace != 0;
+ }
+ }
+ return false;
+}
+
+// Calc methods
+
+// Two little friendships form a secret society
+inline void PrepareLock( SwFlowFrame *pTab )
+{
+ pTab->LockJoin();
+}
+inline void PrepareUnlock( SwFlowFrame *pTab )
+{
+ pTab->UnlockJoin();
+
+}
+
+// hopefully, one day this function simply will return 'false'
+static bool lcl_IsCalcUpperAllowed( const SwFrame& rFrame )
+{
+ return !rFrame.GetUpper()->IsSctFrame() &&
+ !rFrame.GetUpper()->IsFooterFrame() &&
+ // No format of upper Writer fly frame
+ !rFrame.GetUpper()->IsFlyFrame() &&
+ !( rFrame.GetUpper()->IsTabFrame() && rFrame.GetUpper()->GetUpper()->IsInTab() ) &&
+ !( rFrame.IsTabFrame() && rFrame.GetUpper()->IsInTab() );
+}
+
+/** Prepares the Frame for "formatting" (MakeAll()).
+ *
+ * This method serves to save stack space: To calculate the position of the Frame
+ * we have to make sure that the positions of Upper and Prev respectively are
+ * valid. This may require a recursive call (a loop would be quite expensive,
+ * as it's not required very often).
+ *
+ * Every call of MakeAll requires around 500 bytes on the stack - you easily
+ * see where this leads to. This method requires only a little bit of stack
+ * space, so the recursive call should not be a problem here.
+ *
+ * Another advantage is that one nice day, this method and with it the
+ * formatting of predecessors could be avoided. Then it could probably be
+ * possible to jump "quickly" to the document's end.
+ *
+ * @see MakeAll()
+ */
+void SwFrame::PrepareMake(vcl::RenderContext* pRenderContext)
+{
+ StackHack aHack;
+ if ( GetUpper() )
+ {
+ SwFrameDeleteGuard aDeleteGuard(this);
+ if ( lcl_IsCalcUpperAllowed( *this ) )
+ GetUpper()->Calc(pRenderContext);
+ OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
+ if ( !GetUpper() )
+ return;
+
+ const bool bCnt = IsContentFrame();
+ const bool bTab = IsTabFrame();
+ bool bNoSect = IsInSct();
+ bool bOldTabLock = false, bFoll = false;
+ SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;
+
+ if ( bTab )
+ {
+ pThis = static_cast<SwTabFrame*>(this);
+ bOldTabLock = static_cast<SwTabFrame*>(this)->IsJoinLocked();
+ ::PrepareLock( static_cast<SwTabFrame*>(this) );
+ bFoll = pThis->IsFollow();
+ }
+ else if( IsSctFrame() )
+ {
+ pThis = static_cast<SwSectionFrame*>(this);
+ bFoll = pThis->IsFollow();
+ bNoSect = false;
+ }
+ else if ( bCnt )
+ {
+ bFoll = pThis->IsFollow();
+ if ( bFoll && GetPrev() )
+ {
+ // Do not follow the chain when we need only one instance
+ const SwTextFrame* pMaster = static_cast<SwContentFrame*>(this)->FindMaster();
+ if ( pMaster && pMaster->IsLocked() )
+ {
+ MakeAll(pRenderContext);
+ return;
+ }
+ }
+ }
+
+ // There is no format of previous frame, if current frame is a table
+ // frame and its previous frame wants to keep with it.
+ const bool bFormatPrev = !bTab ||
+ !GetPrev() ||
+ !GetPrev()->GetAttrSet()->GetKeep().GetValue();
+ if ( bFormatPrev )
+ {
+ SwFrame *pFrame = GetUpper()->Lower();
+ while ( pFrame != this )
+ {
+ OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." );
+ if ( !pFrame )
+ return; //Oioioioi ...
+
+ if ( !pFrame->isFrameAreaDefinitionValid() )
+ {
+ // A small interference that hopefully improves on the stability:
+ // If I'm Follow AND neighbor of a Frame before me, it would delete
+ // me when formatting. This as you can see could easily become a
+ // confusing situation that we want to avoid.
+ if ( bFoll && pFrame->IsFlowFrame() &&
+ SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) )
+ break;
+
+ bool const isLast(pFrame->GetNext() == this);
+ // note: this seems obvious but does *not* hold, a MakeAll()
+ // could move more than 1 frame backwards!
+ // that's why FindNext() is used below
+ // assert(pFrame->GetUpper() == GetUpper());
+ pFrame->MakeAll(pRenderContext);
+ if( IsSctFrame() && !static_cast<SwSectionFrame*>(this)->GetSection() )
+ break;
+ if (isLast && pFrame->GetUpper() != GetUpper())
+ {
+ assert(GetUpper()->Lower() == this
+ // empty section frames are created all the time...
+ || GetUpper()->Lower()->IsSctFrame()
+ // tab frame/section frame may split multiple times
+ || ( SwFlowFrame::CastFlowFrame(pFrame)
+ && SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
+ && SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
+ SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
+ && (GetUpper()->Lower()->GetNext() == this
+ // if it's more than 10 pages long...
+ || (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
+ == SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
+ && GetUpper()->Lower()->GetNext()->GetNext() == this)
+ // pre-existing empty section frames may end up between them...
+ || GetUpper()->Lower()->GetNext()->IsSctFrame())));
+ break; // tdf#119109 frame was moved backward, prevent
+ // FindNext() returning a frame inside this if
+ } // this is a table!
+ }
+ // With ContentFrames, the chain may be broken while walking through
+ // it. Therefore we have to figure out the next frame in a bit more
+ // complicated way. However, I'll HAVE to get back to myself
+ // sometime again.
+ pFrame = pFrame->FindNext();
+
+ // If we started out in a SectionFrame, it might have happened that
+ // we landed in a Section Follow via the MakeAll calls.
+ // FindNext only gives us the SectionFrame, not it's content - we
+ // won't find ourselves anymore!
+ if( bNoSect && pFrame && pFrame->IsSctFrame() )
+ {
+ SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pCnt )
+ pFrame = pCnt;
+ }
+ }
+ OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." );
+ if ( !GetUpper() )
+ return;
+
+ if ( lcl_IsCalcUpperAllowed( *this ) )
+ GetUpper()->Calc(pRenderContext);
+
+ OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
+ }
+
+ if ( bTab && !bOldTabLock )
+ ::PrepareUnlock( static_cast<SwTabFrame*>(this) );
+ }
+ MakeAll(pRenderContext);
+}
+
+void SwFrame::OptPrepareMake()
+{
+ // #i23129#, #i36347# - no format of upper Writer fly frame
+ if ( GetUpper() && !GetUpper()->IsFooterFrame() &&
+ !GetUpper()->IsFlyFrame() )
+ {
+ {
+ SwFrameDeleteGuard aDeleteGuard(this);
+ GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
+ }
+ OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
+ if ( !GetUpper() )
+ return;
+ }
+ if ( GetPrev() && !GetPrev()->isFrameAreaDefinitionValid() )
+ {
+ PrepareMake(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
+ }
+ else
+ {
+ StackHack aHack;
+ MakeAll(IsRootFrame() ? nullptr : getRootFrame()->GetCurrShell()->GetOut());
+ }
+}
+
+void SwFrame::PrepareCursor()
+{
+ StackHack aHack;
+ if( GetUpper() && !GetUpper()->IsSctFrame() )
+ {
+ const bool bCnt = IsContentFrame();
+ const bool bTab = IsTabFrame();
+ bool bNoSect = IsInSct();
+
+ std::optional<FlowFrameJoinLockGuard> tabGuard;
+ std::optional<SwFrameDeleteGuard> rowGuard;
+ SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;
+
+ if ( bTab )
+ {
+ tabGuard.emplace(static_cast<SwTabFrame*>(this)); // tdf#125741
+ pThis = static_cast<SwTabFrame*>(this);
+ }
+ else if (IsRowFrame())
+ {
+ rowGuard.emplace(this); // tdf#125741 keep this alive
+ }
+ else if( IsSctFrame() )
+ {
+ pThis = static_cast<SwSectionFrame*>(this);
+ bNoSect = false;
+ }
+
+ GetUpper()->PrepareCursor();
+ GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
+
+ OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
+ if ( !GetUpper() )
+ return;
+
+ bool const bFoll = pThis && pThis->IsFollow();
+
+ SwFrame *pFrame = GetUpper()->Lower();
+ while ( pFrame != this )
+ {
+ OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." );
+ if ( !pFrame )
+ return; //Oioioioi ...
+
+ if ( !pFrame->isFrameAreaDefinitionValid() )
+ {
+ // A small interference that hopefully improves on the stability:
+ // If I'm Follow AND neighbor of a Frame before me, it would delete
+ // me when formatting. This as you can see could easily become a
+ // confusing situation that we want to avoid.
+ if ( bFoll && pFrame->IsFlowFrame() &&
+ SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) )
+ break;
+
+ bool const isLast(pFrame->GetNext() == this);
+ pFrame->MakeAll(getRootFrame()->GetCurrShell()->GetOut());
+ if (isLast && pFrame->GetUpper() != GetUpper())
+ {
+ assert(GetUpper()->Lower() == this
+ // empty section frames are created all the time...
+ || GetUpper()->Lower()->IsSctFrame()
+ // tab frame/section frame may split multiple times
+ || ( SwFlowFrame::CastFlowFrame(pFrame)
+ && SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
+ && SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
+ SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
+ && (GetUpper()->Lower()->GetNext() == this
+ // if it's more than 10 pages long...
+ || (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
+ == SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
+ && GetUpper()->Lower()->GetNext()->GetNext() == this)
+ // pre-existing empty section frames may end up between them...
+ || GetUpper()->Lower()->GetNext()->IsSctFrame())));
+ break; // tdf#119109 frame was moved backward, prevent
+ // FindNext() returning a frame inside this if
+ } // this is a table!
+ }
+ // With ContentFrames, the chain may be broken while walking through
+ // it. Therefore we have to figure out the next frame in a bit more
+ // complicated way. However, I'll HAVE to get back to myself
+ // sometime again.
+ pFrame = pFrame->FindNext();
+ if( bNoSect && pFrame && pFrame->IsSctFrame() )
+ {
+ SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pCnt )
+ pFrame = pCnt;
+ }
+ }
+ OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." );
+ if ( !GetUpper() )
+ return;
+
+ GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
+
+ OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
+ }
+ Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
+}
+
+// Here we return GetPrev(); however we will ignore empty SectionFrames
+static SwFrame* lcl_Prev( SwFrame* pFrame, bool bSectPrv = true )
+{
+ SwFrame* pRet = pFrame->GetPrev();
+ if( !pRet && pFrame->GetUpper() && pFrame->GetUpper()->IsSctFrame() &&
+ bSectPrv && !pFrame->IsColumnFrame() )
+ pRet = pFrame->GetUpper()->GetPrev();
+ while( pRet && pRet->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pRet)->GetSection() )
+ pRet = pRet->GetPrev();
+ return pRet;
+}
+
+static SwFrame* lcl_NotHiddenPrev( SwFrame* pFrame )
+{
+ SwFrame *pRet = pFrame;
+ do
+ {
+ pRet = lcl_Prev( pRet );
+ } while ( pRet && pRet->IsTextFrame() && static_cast<SwTextFrame*>(pRet)->IsHiddenNow() );
+ return pRet;
+}
+
+void SwFrame::MakePos()
+{
+ if ( isFrameAreaPositionValid() )
+ return;
+
+ setFrameAreaPositionValid(true);
+ bool bUseUpper = false;
+ SwFrame* pPrv = lcl_Prev( this );
+ if ( pPrv &&
+ ( !pPrv->IsContentFrame() ||
+ ( static_cast<SwContentFrame*>(pPrv)->GetFollow() != this ) )
+ )
+ {
+ if ( !StackHack::IsLocked() &&
+ ( !IsInSct() || IsSctFrame() ) &&
+ !pPrv->IsSctFrame() &&
+ !pPrv->GetAttrSet()->GetKeep().GetValue()
+ )
+ {
+ pPrv->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr); // This may cause Prev to vanish!
+ }
+ else if ( pPrv->getFrameArea().Top() == 0 )
+ {
+ bUseUpper = true;
+ }
+ }
+
+ pPrv = lcl_Prev( this, false );
+ const SwFrameType nMyType = GetType();
+ SwRectFnSet aRectFnSet((IsCellFrame() && GetUpper() ? GetUpper() : this));
+ if ( !bUseUpper && pPrv )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos( pPrv->getFrameArea().Pos() );
+
+ if( FRM_NEIGHBOUR & nMyType )
+ {
+ const bool bR2L = IsRightToLeft();
+
+ if( bR2L )
+ {
+ aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
+ }
+ else
+ {
+ aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
+ }
+
+ // cells may now leave their uppers
+ if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
+ }
+ }
+ else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
+ {
+ if ( aRectFnSet.IsVertL2R() )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() + pPrv->getFrameArea().Width());
+ }
+ else
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
+ }
+ }
+ else
+ {
+ aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
+ }
+ }
+ else if ( GetUpper() )
+ {
+ // If parent frame is a footer frame and its <ColLocked()>, then
+ // do *not* calculate it.
+ // NOTE: Footer frame is <ColLocked()> during its
+ // <FormatSize(..)>, which is called from <Format(..)>, which
+ // is called from <MakeAll()>, which is called from <Calc()>.
+ // #i56850#
+ // - no format of upper Writer fly frame, which is anchored
+ // at-paragraph or at-character.
+ if ( !GetUpper()->IsTabFrame() &&
+ !( IsTabFrame() && GetUpper()->IsInTab() ) &&
+ !GetUpper()->IsSctFrame() &&
+ !dynamic_cast<SwFlyAtContentFrame*>(GetUpper()) &&
+ !( GetUpper()->IsFooterFrame() &&
+ GetUpper()->IsColLocked() )
+ )
+ {
+ GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ }
+ pPrv = lcl_Prev( this, false );
+ if ( !bUseUpper && pPrv )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos( pPrv->getFrameArea().Pos() );
+
+ if( FRM_NEIGHBOUR & nMyType )
+ {
+ const bool bR2L = IsRightToLeft();
+
+ if( bR2L )
+ {
+ aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
+ }
+ else
+ {
+ aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
+ }
+
+ // cells may now leave their uppers
+ if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
+ }
+ }
+ else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
+ }
+ else
+ {
+ aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
+ }
+ }
+ else
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos( GetUpper()->getFrameArea().Pos() );
+
+ if( GetUpper()->IsFlyFrame() )
+ {
+ aFrm.Pos() += static_cast<SwFlyFrame*>(GetUpper())->ContentPos();
+ }
+ else
+ {
+ aFrm.Pos() += GetUpper()->getFramePrintArea().Pos();
+ }
+
+ if( FRM_NEIGHBOUR & nMyType && IsRightToLeft() )
+ {
+ if( aRectFnSet.IsVert() )
+ {
+ aFrm.Pos().setY(aFrm.Pos().getY() + GetUpper()->getFramePrintArea().Height() - aFrm.Height());
+ }
+ else
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
+ }
+ }
+ else if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && FRM_NOTE_VERT & nMyType )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + GetUpper()->getFramePrintArea().Width());
+ }
+ }
+ }
+ else
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(0);
+ aFrm.Pos().setY(0);
+ }
+
+ if( IsBodyFrame() && aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && GetUpper() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
+ }
+
+ setFrameAreaPositionValid(true);
+}
+
+// #i28701# - new type <SwSortedObjs>
+static void lcl_CheckObjects(SwSortedObjs& rSortedObjs, const SwFrame* pFrame, tools::Long& rBot)
+{
+ // And then there can be paragraph anchored frames that sit below their paragraph.
+ tools::Long nMax = 0;
+ for (SwAnchoredObject* pObj : rSortedObjs)
+ {
+ // #i28701# - consider changed type of <SwSortedObjs>
+ // entries.
+ tools::Long nTmp = 0;
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if( pFly->getFrameArea().Top() != FAR_AWAY &&
+ ( pFrame->IsPageFrame() ? pFly->IsFlyLayFrame() :
+ ( pFly->IsFlyAtContentFrame() &&
+ ( pFrame->IsBodyFrame() ? pFly->GetAnchorFrame()->IsInDocBody() :
+ pFly->GetAnchorFrame()->IsInFootnote() ) ) ) )
+ {
+ nTmp = pFly->getFrameArea().Bottom();
+ }
+ }
+ else
+ nTmp = pObj->GetObjRect().Bottom();
+ nMax = std::max( nTmp, nMax );
+ }
+ ++nMax; // Lower edge vs. height!
+ rBot = std::max( rBot, nMax );
+}
+
+size_t SwPageFrame::GetContentHeight(const tools::Long nTop, const tools::Long nBottom) const
+{
+ OSL_ENSURE(!(FindBodyCont() && FindBodyCont()->Lower() && FindBodyCont()->Lower()->IsColumnFrame()),
+ "SwPageFrame::GetContentHeight(): No support for columns.");
+
+ // In pages without columns, the content defines the size.
+ tools::Long nBot = getFrameArea().Top() + nTop;
+ const SwFrame *pFrame = Lower();
+ while (pFrame)
+ {
+ tools::Long nTmp = 0;
+ const SwFrame *pCnt = static_cast<const SwLayoutFrame*>(pFrame)->ContainsAny();
+ while (pCnt && (pCnt->GetUpper() == pFrame ||
+ static_cast<const SwLayoutFrame*>(pFrame)->IsAnLower(pCnt)))
+ {
+ nTmp += pCnt->getFrameArea().Height();
+ if (pCnt->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pCnt)->IsUndersized())
+ {
+ // This TextFrame would like to be a bit bigger.
+ nTmp += static_cast<const SwTextFrame*>(pCnt)->GetParHeight()
+ - pCnt->getFramePrintArea().Height();
+ }
+ else if (pCnt->IsSctFrame())
+ {
+ // Grow if undersized, but don't shrink if oversized.
+ const auto delta = static_cast<const SwSectionFrame*>(pCnt)->CalcUndersize();
+ if (delta > 0)
+ nTmp += delta;
+ }
+
+ pCnt = pCnt->FindNext();
+ }
+ // Consider invalid body frame properties
+ if (pFrame->IsBodyFrame() &&
+ (!pFrame->isFrameAreaSizeValid() ||
+ !pFrame->isFramePrintAreaValid()) &&
+ (pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height())
+ )
+ {
+ nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
+ }
+ else
+ {
+ // Assert invalid lower property
+ OSL_ENSURE(!(pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height()),
+ "SwPageFrame::GetContentHeight(): Lower with frame height < printing height");
+ nTmp += pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
+ }
+ if (!pFrame->IsBodyFrame())
+ nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
+ nBot += nTmp;
+ // Here we check whether paragraph anchored objects
+ // protrude outside the Body/FootnoteCont.
+ if (m_pSortedObjs && !pFrame->IsHeaderFrame() &&
+ !pFrame->IsFooterFrame())
+ lcl_CheckObjects(*m_pSortedObjs, pFrame, nBot);
+ pFrame = pFrame->GetNext();
+ }
+ nBot += nBottom;
+ // And the page anchored ones
+ if (m_pSortedObjs)
+ lcl_CheckObjects(*m_pSortedObjs, this, nBot);
+ nBot -= getFrameArea().Top();
+
+ return nBot;
+}
+
+void SwPageFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
+
+ const SwRect aOldRect( getFrameArea() ); // Adjust root size
+ const SwLayNotify aNotify( this ); // takes care of the notification in the dtor
+ std::optional<SwBorderAttrAccess> oAccess;
+ const SwBorderAttrs*pAttrs = nullptr;
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ if ( !isFrameAreaPositionValid() )
+ {
+ setFrameAreaPositionValid(true); // positioning of the pages is taken care of by the root frame
+ }
+
+ if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ if ( IsEmptyPage() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( 0 );
+ aFrm.Height( 0 );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( 0 );
+ aPrt.Height( 0 );
+ aPrt.Left( 0 );
+ aPrt.Top( 0 );
+
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ }
+ else
+ {
+ if (!oAccess)
+ {
+ oAccess.emplace(SwFrame::GetCache(), this);
+ pAttrs = oAccess->Get();
+ }
+ assert(pAttrs);
+
+ SwRootFrame* pRootFrame = getRootFrame();
+ SwViewShell* pSh = pRootFrame->GetCurrShell();
+ if (pSh && pSh->GetViewOptions()->getBrowseMode())
+ {
+ // In BrowseView, we use fixed settings
+ const Size aBorder = pRenderContext->PixelToLogic( pSh->GetBrowseBorder() );
+ const tools::Long nTop = pAttrs->CalcTopLine() + aBorder.Height();
+ const tools::Long nBottom = pAttrs->CalcBottomLine()+ aBorder.Height();
+
+ tools::Long nWidth = GetUpper() ? static_cast<SwRootFrame*>(GetUpper())->GetBrowseWidth() : 0;
+ const auto nDefWidth = pSh->GetBrowseWidth();
+ if (nWidth < nDefWidth)
+ nWidth = nDefWidth;
+ nWidth += + 2 * aBorder.Width();
+
+ constexpr tools::Long constTwips_2cm = o3tl::toTwips(2, o3tl::Length::cm);
+ nWidth = std::max(nWidth, 2L * aBorder.Width() + constTwips_2cm);
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( nWidth );
+
+ SwLayoutFrame *pBody = FindBodyCont();
+ if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ {
+ // Columns have a fixed height
+ aFrm.Height( pAttrs->GetSize().Height() );
+ }
+ else
+ {
+ // In pages without columns, the content defines the size.
+ tools::Long nBot = GetContentHeight(nTop, nBottom);
+
+ // #i35143# - If second page frame
+ // exists, the first page doesn't have to fulfill the
+ // visible area.
+ if ( !GetPrev() && !GetNext() )
+ {
+ nBot = std::max( nBot, pSh->VisArea().Height() );
+ }
+ // #i35143# - Assure, that the page
+ // doesn't exceed the defined browse height.
+ aFrm.Height( std::min( nBot, BROWSE_HEIGHT ) );
+ }
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Left ( pAttrs->CalcLeftLine() + aBorder.Width() );
+ aPrt.Top ( nTop );
+ aPrt.Width( getFrameArea().Width() - ( aPrt.Left() + pAttrs->CalcRightLine() + aBorder.Width() ) );
+ aPrt.Height( getFrameArea().Height() - (nTop + nBottom) );
+ }
+
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ continue;
+ }
+ else if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden() && pRootFrame->GetLastPage() != this)
+ {
+ tools::Long height = 0;
+ SwLayoutFrame *pBody = FindBodyCont();
+ if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ {
+ // Columns have a fixed height
+ height = pAttrs->GetSize().Height();
+ }
+ else
+ {
+ // No need for borders.
+ height = GetContentHeight(0, 0);
+ }
+
+ if (height > 0)
+ {
+ ChgSize(Size(getFrameArea().Width(), height));
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Top(0);
+ aPrt.Height(height);
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ continue;
+ }
+
+ // Fallback to default formatting. Especially relevant
+ // when loading a doc when Hide Whitespace is enabled.
+ // Heights are zero initially.
+ }
+
+ // Set FixSize. For pages, this is not done from Upper, but from
+ // the attribute.
+ //FIXME: This resets the size when (isFrameAreaSizeValid() && !isFramePrintAreaValid()).
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.SSize( pAttrs->GetSize() );
+ }
+ Format( pRenderContext, pAttrs );
+ }
+ }
+ } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+
+ if ( getFrameArea() != aOldRect && GetUpper() )
+ static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
+
+ OSL_ENSURE( !GetUpper() || GetUpper()->getFramePrintArea().Width() >= getFrameArea().Width(),
+ "Upper (Root) must be wide enough to contain the widest page");
+}
+
+void SwLayoutFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
+{
+ PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
+
+ // takes care of the notification in the dtor
+ const SwLayNotify aNotify( this );
+ bool bVert = IsVertical();
+
+ SwRectFn fnRect = ( IsNeighbourFrame() == bVert )? fnRectHori : ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert );
+
+ std::optional<SwBorderAttrAccess> oAccess;
+ const SwBorderAttrs*pAttrs = nullptr;
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ if ( !isFrameAreaPositionValid() )
+ MakePos();
+
+ if ( GetUpper() )
+ {
+ // NEW TABLES
+ if ( IsLeaveUpperAllowed() )
+ {
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFramePrintAreaValid(false);
+ }
+ }
+ else
+ {
+ if ( !isFrameAreaSizeValid() )
+ {
+ // Set FixSize; VarSize is set by Format() after calculating the PrtArea
+ setFramePrintAreaValid(false);
+
+ SwTwips nPrtWidth = (GetUpper()->getFramePrintArea().*fnRect->fnGetWidth)();
+ if( bVert && ( IsBodyFrame() || IsFootnoteContFrame() ) )
+ {
+ SwFrame* pNxt = GetPrev();
+ while( pNxt && !pNxt->IsHeaderFrame() )
+ pNxt = pNxt->GetPrev();
+ if( pNxt )
+ nPrtWidth -= pNxt->getFrameArea().Height();
+ pNxt = GetNext();
+ while( pNxt && !pNxt->IsFooterFrame() )
+ pNxt = pNxt->GetNext();
+ if( pNxt )
+ nPrtWidth -= pNxt->getFrameArea().Height();
+ }
+
+ const tools::Long nDiff = nPrtWidth - (getFrameArea().*fnRect->fnGetWidth)();
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ // SwRectFn switched between horizontal and vertical when bVert == IsNeighbourFrame().
+ // We pick fnSubLeft or fnAddRight that is correspondant to SwRectFn->fnAddBottom
+ if( ( IsCellFrame() && IsRightToLeft() ) || ( IsColumnFrame() && bVert && !IsVertLR() ) )
+ {
+ (aFrm.*fnRect->fnSubLeft)( nDiff );
+ }
+ else
+ {
+ (aFrm.*fnRect->fnAddRight)( nDiff );
+ }
+ }
+ else
+ {
+ // Don't leave your upper
+ const SwTwips nDeadLine = (GetUpper()->*fnRect->fnGetPrtBottom)();
+ if( (getFrameArea().*fnRect->fnOverStep)( nDeadLine ) )
+ {
+ setFrameAreaSizeValid(false);
+ }
+ }
+ }
+ }
+
+ if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ if ( !oAccess )
+ {
+ oAccess.emplace(SwFrame::GetCache(), this);
+ pAttrs = oAccess->Get();
+ }
+ Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
+ }
+ } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+}
+
+bool SwTextNode::IsCollapse() const
+{
+ if (GetDoc().GetDocumentSettingManager().get( DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA )
+ && GetText().isEmpty())
+ {
+ SwNodeOffset nIdx=GetIndex();
+ const SwEndNode *pNdBefore=GetNodes()[nIdx-1]->GetEndNode();
+ const SwEndNode *pNdAfter=GetNodes()[nIdx+1]->GetEndNode();
+
+ // The paragraph is collapsed only if the NdAfter is the end of a cell
+ bool bInTable = FindTableNode( ) != nullptr;
+
+ SwSortedObjs* pObjs = getLayoutFrame( GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )->GetDrawObjs( );
+ const size_t nObjs = ( pObjs != nullptr ) ? pObjs->size( ) : 0;
+
+ return pNdBefore!=nullptr && pNdAfter!=nullptr && nObjs == 0 && bInTable;
+ }
+
+ return false;
+}
+
+bool SwFrame::IsCollapse() const
+{
+ if (!IsTextFrame())
+ return false;
+
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>(this);
+ const SwTextNode *pTextNode = pTextFrame->GetTextNodeForParaProps();
+ // TODO this SwTextNode function is pointless and should be merged in here
+ return pTextFrame->GetText().isEmpty() && pTextNode && pTextNode->IsCollapse();
+}
+
+void SwContentFrame::MakePrtArea( const SwBorderAttrs &rAttrs )
+{
+ if ( isFramePrintAreaValid() )
+ return;
+
+ setFramePrintAreaValid(true);
+ SwRectFnSet aRectFnSet(this);
+ const bool bTextFrame = IsTextFrame();
+ SwTwips nUpper = 0;
+ if ( bTextFrame && static_cast<SwTextFrame*>(this)->IsHiddenNow() )
+ {
+ if ( static_cast<SwTextFrame*>(this)->HasFollow() )
+ static_cast<SwTextFrame*>(this)->JoinFrame();
+
+ if( aRectFnSet.GetHeight(getFramePrintArea()) )
+ {
+ static_cast<SwTextFrame*>(this)->HideHidden();
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Pos().setX(0);
+ aPrt.Pos().setY(0);
+ aRectFnSet.SetWidth( aPrt, aRectFnSet.GetWidth(getFrameArea()) );
+ aRectFnSet.SetHeight( aPrt, 0 );
+ }
+
+ nUpper = -( aRectFnSet.GetHeight(getFrameArea()) );
+ }
+ else
+ {
+ // Simplification: ContentFrames are always variable in height!
+
+ // At the FixSize, the surrounding Frame enforces the size;
+ // the borders are simply subtracted.
+ const tools::Long nLeft = rAttrs.CalcLeft( this );
+ const tools::Long nRight = rAttrs.CalcRight( this );
+ aRectFnSet.SetXMargins( *this, nLeft, nRight );
+
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ SwTwips nWidthArea;
+ if( pSh && 0!=(nWidthArea=aRectFnSet.GetWidth(pSh->VisArea())) &&
+ GetUpper()->IsPageBodyFrame() && // but not for BodyFrames in Columns
+ pSh->GetViewOptions()->getBrowseMode() )
+ {
+ // Do not protrude the edge of the visible area. The page may be
+ // wider, because there may be objects with excess width
+ // (RootFrame::ImplCalcBrowseWidth())
+ tools::Long nMinWidth = 0;
+
+ for (size_t i = 0; GetDrawObjs() && i < GetDrawObjs()->size(); ++i)
+ {
+ // #i28701# - consider changed type of
+ // <SwSortedObjs> entries
+ SwAnchoredObject* pObj = (*GetDrawObjs())[i];
+ const SwFrameFormat& rFormat = pObj->GetFrameFormat();
+ const bool bFly = pObj->DynCastFlyFrame() != nullptr;
+ if ((bFly && (FAR_AWAY == pObj->GetObjRect().Width()))
+ || rFormat.GetFrameSize().GetWidthPercent())
+ {
+ continue;
+ }
+
+ if ( RndStdIds::FLY_AS_CHAR == rFormat.GetAnchor().GetAnchorId() )
+ {
+ nMinWidth = std::max( nMinWidth,
+ bFly ? rFormat.GetFrameSize().GetWidth()
+ : pObj->GetObjRect().Width() );
+ }
+ }
+
+ const Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
+ tools::Long nWidth = nWidthArea - 2 * ( IsVertical() ? aBorder.Height() : aBorder.Width() );
+ nWidth -= aRectFnSet.GetLeft(getFramePrintArea());
+ nWidth -= rAttrs.CalcRightLine();
+ nWidth = std::max( nMinWidth, nWidth );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetWidth( aPrt, std::min( nWidth, aRectFnSet.GetWidth(aPrt) ) );
+ }
+
+ if ( aRectFnSet.GetWidth(getFramePrintArea()) <= MINLAY )
+ {
+ // The PrtArea should already be at least MINLAY wide, matching the
+ // minimal values of the UI
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetWidth( aPrt, std::min( tools::Long(MINLAY), aRectFnSet.GetWidth(getFrameArea()) ) );
+ SwTwips nTmp = aRectFnSet.GetWidth(getFrameArea()) - aRectFnSet.GetWidth(aPrt);
+
+ if( aRectFnSet.GetLeft(aPrt) > nTmp )
+ {
+ aRectFnSet.SetLeft( aPrt, nTmp );
+ }
+ }
+
+ // The following rules apply for VarSize:
+ // 1. The first entry of a chain has no top border
+ // 2. There is never a bottom border
+ // 3. The top border is the maximum of the distance
+ // of Prev downwards and our own distance upwards
+ // Those three rules apply when calculating spacings
+ // that are given by UL- and LRSpace. There might be a spacing
+ // in all directions however; this may be caused by borders
+ // and / or shadows.
+ // 4. The spacing for TextFrames corresponds to the interline lead,
+ // at a minimum.
+
+ nUpper = CalcUpperSpace( &rAttrs );
+
+ SwTwips nLower = CalcLowerSpace( &rAttrs );
+ if (IsCollapse()) {
+ nUpper=0;
+ nLower=0;
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetPosY( aPrt, !aRectFnSet.IsVert() ? nUpper : nLower);
+ }
+
+ nUpper += nLower;
+ nUpper -= aRectFnSet.GetHeight(getFrameArea()) - aRectFnSet.GetHeight(getFramePrintArea());
+ }
+ // If there's a difference between old and new size, call Grow() or
+ // Shrink() respectively.
+ if ( nUpper )
+ {
+ if ( nUpper > 0 )
+ GrowFrame( nUpper );
+ else
+ ShrinkFrame( -nUpper );
+ }
+}
+
+#define STOP_FLY_FORMAT 10
+// - loop prevention
+const int cnStopFormat = 15;
+
+inline void ValidateSz( SwFrame *pFrame )
+{
+ if ( pFrame )
+ {
+ pFrame->setFrameAreaSizeValid(true);
+ pFrame->setFramePrintAreaValid(true);
+ }
+}
+
+void SwContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
+{
+ OSL_ENSURE( GetUpper(), "no Upper?" );
+ OSL_ENSURE( IsTextFrame(), "MakeAll(), NoText" );
+
+ if ( !IsFollow() && StackHack::IsLocked() )
+ return;
+
+ if ( IsJoinLocked() )
+ return;
+
+ OSL_ENSURE( !static_cast<SwTextFrame*>(this)->IsSwapped(), "Calculation of a swapped frame" );
+
+ StackHack aHack;
+
+ if ( static_cast<SwTextFrame*>(this)->IsLocked() )
+ {
+ OSL_FAIL( "Format for locked TextFrame." );
+ return;
+ }
+
+ std::optional<SwFrameDeleteGuard> oDeleteGuard(std::in_place, this);
+ LockJoin();
+ tools::Long nFormatCount = 0;
+ // - loop prevention
+ int nConsecutiveFormatsWithoutChange = 0;
+ PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
+
+ // takes care of the notification in the dtor
+ std::optional<SwContentNotify> oNotify( std::in_place, this );
+
+ // as long as bMakePage is true, a new page can be created (exactly once)
+ bool bMakePage = true;
+ // bMovedBwd gets set to true when the frame flows backwards
+ bool bMovedBwd = false;
+ // as long as bMovedFwd is false, the Frame may flow backwards (until
+ // it has been moved forward once)
+ bool bMovedFwd = false;
+ sal_Bool bFormatted = false; // For the widow/orphan rules, we encourage the
+ // last ContentFrame of a chain to format. This only
+ // needs to happen once. Every time the Frame is
+ // moved, the flag will have to be reset.
+ bool bMustFit = false; // Once the emergency brake is pulled,
+ // no other prepares will be triggered
+ bool bFitPromise = false; // If a paragraph didn't fit, but promises
+ // with WouldFit that it would adjust accordingly,
+ // this flag is set. If it turns out that it
+ // didn't keep it's promise, we can act in a
+ // controlled fashion.
+ const bool bFly = IsInFly();
+ const bool bTab = IsInTab();
+ const bool bFootnote = IsInFootnote();
+ const bool bSct = IsInSct();
+ Point aOldFramePos; // This is so we can compare with the last pos
+ Point aOldPrtPos; // and determine whether it makes sense to Prepare
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ if ( !IsFollow() && rAttrs.JoinedWithPrev( *(this) ) )
+ {
+ oNotify->SetBordersJoinedWithPrev();
+ }
+
+ const bool bKeep = IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem());
+
+ std::unique_ptr<SwSaveFootnoteHeight> pSaveFootnote;
+ if ( bFootnote )
+ {
+ SwFootnoteFrame *pFootnote = FindFootnoteFrame();
+ SwSectionFrame* pSct = pFootnote->FindSctFrame();
+ if ( !static_cast<SwTextFrame*>(pFootnote->GetRef())->IsLocked() )
+ {
+ SwFootnoteBossFrame* pBoss = pFootnote->GetRef()->FindFootnoteBossFrame(
+ pFootnote->GetAttr()->GetFootnote().IsEndNote() );
+ if( !pSct || pSct->IsColLocked() || !pSct->Growable() )
+ pSaveFootnote.reset( new SwSaveFootnoteHeight( pBoss,
+ static_cast<SwTextFrame*>(pFootnote->GetRef())->GetFootnoteLine( pFootnote->GetAttr() ) ) );
+ }
+ }
+
+ if ( GetUpper()->IsSctFrame() &&
+ HasFollow() && !GetFollow()->IsDeleteForbidden() &&
+ &GetFollow()->GetFrame() == GetNext() )
+ {
+ static_cast<SwTextFrame&>(*this).JoinFrame();
+ }
+
+ // #i28701# - move master forward, if it has to move,
+ // because of its object positioning.
+ if ( !static_cast<SwTextFrame*>(this)->IsFollow() )
+ {
+ sal_uInt32 nToPageNum = 0;
+ const bool bMoveFwdByObjPos = SwLayouter::FrameMovedFwdByObjPos(
+ *(GetAttrSet()->GetDoc()),
+ *static_cast<SwTextFrame*>(this),
+ nToPageNum );
+ // #i58182#
+ // Also move a paragraph forward, which is the first one inside a table cell.
+ if ( bMoveFwdByObjPos &&
+ FindPageFrame()->GetPhyPageNum() < nToPageNum &&
+ ( lcl_Prev( this ) ||
+ GetUpper()->IsCellFrame() ||
+ ( GetUpper()->IsSctFrame() &&
+ GetUpper()->GetUpper()->IsCellFrame() ) ) &&
+ IsMoveable() )
+ {
+ bMovedFwd = true;
+ MoveFwd( bMakePage, false );
+ }
+ }
+
+ // If a Follow sits next to its Master and doesn't fit, we know it can
+ // be moved right now.
+ if ( lcl_Prev( this ) && static_cast<SwTextFrame*>(this)->IsFollow() && IsMoveable() )
+ {
+ bMovedFwd = true;
+ // If follow frame is in table, its master will be the last in the
+ // current table cell. Thus, invalidate the printing area of the master.
+ if ( IsInTab() )
+ {
+ lcl_Prev( this )->InvalidatePrt();
+ }
+ MoveFwd( bMakePage, false );
+ }
+
+ // Check footnote content for forward move.
+ // If a content of a footnote is on a prior page/column as its invalid
+ // reference, it can be moved forward.
+ if ( bFootnote && !isFrameAreaPositionValid() )
+ {
+ SwFootnoteFrame* pFootnote = FindFootnoteFrame();
+ SwContentFrame* pRefCnt = pFootnote ? pFootnote->GetRef() : nullptr;
+
+ if ( pRefCnt && !pRefCnt->isFrameAreaDefinitionValid() )
+ {
+ SwFootnoteBossFrame* pFootnoteBossOfFootnote = pFootnote->FindFootnoteBossFrame();
+ SwFootnoteBossFrame* pFootnoteBossOfRef = pRefCnt->FindFootnoteBossFrame();
+ //<loop of movefwd until condition held or no move>
+ if ( pFootnoteBossOfFootnote && pFootnoteBossOfRef &&
+ pFootnoteBossOfFootnote != pFootnoteBossOfRef &&
+ pFootnoteBossOfFootnote->IsBefore( pFootnoteBossOfRef ) )
+ {
+ bMovedFwd = true;
+ MoveFwd( bMakePage, false );
+ }
+ }
+ }
+
+ SwRectFnSet aRectFnSet(this);
+
+ SwFrame const* pMoveBwdPre(nullptr);
+ bool isMoveBwdPreValid(false);
+
+ SwRect aOldFrame_StopFormat, aOldFrame_StopFormat2;
+ SwRect aOldPrt_StopFormat, aOldPrt_StopFormat2;
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ // - loop prevention
+ aOldFrame_StopFormat2 = aOldFrame_StopFormat;
+ aOldPrt_StopFormat2 = aOldPrt_StopFormat;
+ aOldFrame_StopFormat = getFrameArea();
+ aOldPrt_StopFormat = getFramePrintArea();
+
+ bool bMoveable = IsMoveable();
+ if (bMoveable)
+ {
+ SwFrame *pPre = GetIndPrev();
+ if ( CheckMoveFwd( bMakePage, bKeep, bMovedBwd ) )
+ {
+ aRectFnSet.Refresh(this);
+ bMovedFwd = true;
+ if ( bMovedBwd )
+ {
+ // While flowing back, the Upper was encouraged to
+ // completely re-paint itself. We can skip this now after
+ // flowing back and forth.
+ GetUpper()->ResetCompletePaint();
+ // The predecessor was invalidated, so this is obsolete as well now.
+ assert(pPre);
+ if ((pPre == pMoveBwdPre && isMoveBwdPreValid) && !pPre->IsSctFrame())
+ ::ValidateSz( pPre );
+ }
+ bMoveable = IsMoveable();
+ }
+ }
+
+ aOldFramePos = aRectFnSet.GetPos(getFrameArea());
+ aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
+
+ if ( !isFrameAreaPositionValid() )
+ MakePos();
+
+ //Set FixSize. VarSize is being adjusted by Format().
+ if ( !isFrameAreaSizeValid() )
+ {
+ // invalidate printing area flag, if the following conditions are hold:
+ // - current frame width is 0.
+ // - current printing area width is 0.
+ // - frame width is adjusted to a value greater than 0.
+ // - printing area flag is true.
+ // Thus, it's assured that the printing area is adjusted, if the
+ // frame area width changes its width from 0 to something greater
+ // than 0.
+ // Note: A text frame can be in such a situation, if the format is
+ // triggered by method call <SwCursorShell::SetCursor()> after
+ // loading the document.
+ const SwTwips nNewFrameWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
+
+ if ( isFramePrintAreaValid() &&
+ nNewFrameWidth > 0 &&
+ aRectFnSet.GetWidth(getFrameArea()) == 0 &&
+ aRectFnSet.GetWidth(getFramePrintArea()) == 0 )
+ {
+ setFramePrintAreaValid(false);
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetWidth( aFrm, nNewFrameWidth );
+ }
+
+ // When a lower of a vertically aligned fly frame changes its size we need to recalculate content pos.
+ if( GetUpper() && GetUpper()->IsFlyFrame() &&
+ GetUpper()->GetFormat()->GetTextVertAdjust().GetValue() != SDRTEXTVERTADJUST_TOP )
+ {
+ static_cast<SwFlyFrame*>(GetUpper())->InvalidateContentPos();
+ GetUpper()->SetCompletePaint();
+ }
+ }
+ if ( !isFramePrintAreaValid() )
+ {
+ const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
+ // #i34730# - keep current frame height
+ const SwTwips nOldH = aRectFnSet.GetHeight(getFrameArea());
+ MakePrtArea( rAttrs );
+ if ( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
+ Prepare( PrepareHint::FixSizeChanged );
+ // #i34730# - check, if frame height has changed.
+ // If yes, send a PrepareHint::AdjustSizeWithoutFormatting and invalidate the size flag to
+ // force a format. The format will check in its method
+ // <SwTextFrame::CalcPreps()>, if the already formatted lines still
+ // fit and if not, performs necessary actions.
+ // #i40150# - no check, if frame is undersized.
+ if ( isFrameAreaSizeValid() && !IsUndersized() && nOldH != aRectFnSet.GetHeight(getFrameArea()) )
+ {
+ // #115759# - no PrepareHint::AdjustSizeWithoutFormatting and size
+ // invalidation, if height decreases only by the additional
+ // lower space as last content of a table cell and an existing
+ // follow containing one line exists.
+ const SwTwips nHDiff = nOldH - aRectFnSet.GetHeight(getFrameArea());
+ const bool bNoPrepAdjustFrame =
+ nHDiff > 0 && IsInTab() && GetFollow() &&
+ (1 == static_cast<SwTextFrame*>(GetFollow())->GetLineCount(TextFrameIndex(COMPLETE_STRING))
+ || aRectFnSet.GetWidth(static_cast<SwTextFrame*>(GetFollow())->getFrameArea()) < 0) &&
+ GetFollow()->CalcAddLowerSpaceAsLastInTableCell() == nHDiff;
+ if ( !bNoPrepAdjustFrame )
+ {
+ Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ setFrameAreaSizeValid(false);
+ }
+ }
+ }
+
+ // To make the widow and orphan rules work, we need to notify the ContentFrame.
+ // Criteria:
+ // - It needs to be movable (otherwise, splitting doesn't make sense)
+ // - It needs to overlap with the lower edge of the PrtArea of the Upper
+ if ( !bMustFit )
+ {
+ bool bWidow = true;
+ const SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
+ if( bMoveable && !bFormatted &&
+ ( GetFollow() || aRectFnSet.OverStep( getFrameArea(), nDeadLine ) ) )
+ {
+ Prepare( PrepareHint::WidowsOrphans, nullptr, false );
+ setFrameAreaSizeValid(false);
+ bWidow = false;
+ }
+ if( aRectFnSet.GetPos(getFrameArea()) != aOldFramePos ||
+ aRectFnSet.GetPos(getFramePrintArea()) != aOldPrtPos )
+ {
+ // In this Prepare, an InvalidateSize_() might happen.
+ // isFrameAreaSizeValid() becomes false and Format() gets called.
+ Prepare( PrepareHint::FramePositionChanged, static_cast<const void*>(&bFormatted), false );
+ if ( bWidow && GetFollow() )
+ {
+ Prepare( PrepareHint::WidowsOrphans, nullptr, false );
+ setFrameAreaSizeValid(false);
+ }
+ }
+ }
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFrameAreaSizeValid(true);
+ bFormatted = true;
+ ++nFormatCount;
+ if( nFormatCount > STOP_FLY_FORMAT )
+ SetFlyLock( true );
+ // - loop prevention
+ // No format any longer, if <cnStopFormat> consecutive formats
+ // without change occur.
+ if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
+ {
+ Format(getRootFrame()->GetCurrShell()->GetOut());
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL( "debug assertion: <SwContentFrame::MakeAll()> - format of text frame suppressed by fix b6448963" );
+ }
+#endif
+ }
+
+ // If this is the first one in a chain, check if this can flow
+ // backwards (if this is movable at all).
+ // To prevent oscillations/loops, check that this has not just
+ // flowed forwards.
+ bool bDummy;
+ auto const pTemp(GetIndPrev());
+ auto const bTemp(pTemp && pTemp->isFrameAreaSizeValid()
+ && pTemp->isFramePrintAreaValid());
+ if ( !lcl_Prev( this ) &&
+ !bMovedFwd &&
+ ( bMoveable || ( bFly && !bTab ) ) &&
+ ( !bFootnote || !GetUpper()->FindFootnoteFrame()->GetPrev() )
+ && MoveBwd( bDummy ) )
+ {
+ aRectFnSet.Refresh(this);
+ pMoveBwdPre = pTemp;
+ isMoveBwdPreValid = bTemp;
+ bMovedBwd = true;
+ bFormatted = false;
+ if ( bKeep && bMoveable )
+ {
+ if( CheckMoveFwd( bMakePage, false, bMovedBwd ) )
+ {
+ bMovedFwd = true;
+ bMoveable = IsMoveable();
+ aRectFnSet.Refresh(this);
+ }
+ Point aOldPos = aRectFnSet.GetPos(getFrameArea());
+ MakePos();
+ if( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
+ {
+ Prepare( PrepareHint::FramePositionChanged, static_cast<const void*>(&bFormatted), false );
+ if ( !isFrameAreaSizeValid() )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetWidth( aFrm, aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) );
+ }
+
+ if ( !isFramePrintAreaValid() )
+ {
+ const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
+ MakePrtArea( rAttrs );
+ if( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
+ Prepare( PrepareHint::FixSizeChanged, nullptr, false );
+ }
+ if( GetFollow() )
+ {
+ Prepare( PrepareHint::WidowsOrphans, nullptr, false );
+ }
+
+ setFrameAreaSizeValid(true);
+ bFormatted = true;
+ Format(getRootFrame()->GetCurrShell()->GetOut());
+ }
+ }
+ SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
+ while( pNxt && pNxt->IsSctFrame() )
+ { // Leave empty sections out, go into the other ones.
+ if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
+ {
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
+ if( pTmp )
+ {
+ pNxt = pTmp;
+ break;
+ }
+ }
+ pNxt = pNxt->FindNext();
+ }
+ if ( pNxt )
+ {
+ pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ if( isFrameAreaPositionValid() && !GetIndNext() )
+ {
+ SwSectionFrame *pSct = FindSctFrame();
+ if( pSct && !pSct->isFrameAreaSizeValid() )
+ {
+ SwSectionFrame* pNxtSct = pNxt->FindSctFrame();
+ if( pNxtSct && pSct->IsAnFollow( pNxtSct ) )
+ {
+ setFrameAreaPositionValid(false);
+ }
+ }
+ else
+ {
+ setFrameAreaPositionValid(false);
+ }
+ }
+ }
+ }
+ }
+
+ // In footnotes, the TextFrame may validate itself, which can lead to the
+ // situation that it's position is wrong despite being "valid".
+ if ( isFrameAreaPositionValid() )
+ {
+ // #i59341#
+ // Workaround for inadequate layout algorithm:
+ // suppress invalidation and calculation of position, if paragraph
+ // has formatted itself at least STOP_FLY_FORMAT times and
+ // has anchored objects.
+ // Thus, the anchored objects get the possibility to format itself
+ // and this probably solve the layout loop.
+ if ( bFootnote &&
+ nFormatCount <= STOP_FLY_FORMAT &&
+ !GetDrawObjs() )
+ {
+ setFrameAreaPositionValid(false);
+ MakePos();
+ aOldFramePos = aRectFnSet.GetPos(getFrameArea());
+ aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
+ }
+ }
+
+ // - loop prevention
+ {
+ if ( (aOldFrame_StopFormat == getFrameArea() || aOldFrame_StopFormat2 == getFrameArea() ) &&
+ (aOldPrt_StopFormat == getFramePrintArea() || aOldPrt_StopFormat2 == getFramePrintArea()))
+ {
+ ++nConsecutiveFormatsWithoutChange;
+ }
+ else
+ {
+ nConsecutiveFormatsWithoutChange = 0;
+ }
+ }
+
+ // Yet again an invalid value? Repeat from the start...
+ if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ continue;
+
+ // Done?
+ // Attention: because height == 0, it's better to use Top()+Height() instead of
+ // Bottom(). This might happen with undersized TextFrames on the lower edge of a
+ // multi-column section
+ const tools::Long nPrtBottom = aRectFnSet.GetPrtBottom(*GetUpper());
+ tools::Long nBottomDist = aRectFnSet.BottomDist(getFrameArea(), nPrtBottom);
+
+ // Hide whitespace may require not to insert a new page.
+ SwPageFrame* pPageFrame = FindPageFrame();
+ const bool bHeightValid = pPageFrame->CheckPageHeightValidForHideWhitespace(nBottomDist);
+ if (!bHeightValid)
+ {
+ pPageFrame->InvalidateSize();
+ nBottomDist = 0;
+ }
+
+ if( nBottomDist >= 0 )
+ {
+ if ( bKeep && bMoveable )
+ {
+ // We make sure the successor will be formatted the same.
+ // This way, we keep control until (almost) everything is stable,
+ // allowing us to avoid endless loops caused by ever repeating
+ // retries.
+
+ // bMoveFwdInvalid is required for #38407#. This was originally solved
+ // in flowfrm.cxx rev 1.38, but broke the above schema and
+ // preferred to play towers of hanoi (#43669#).
+ SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
+ // For sections we prefer the content, because it can change
+ // the page if required.
+ while( pNxt && pNxt->IsSctFrame() )
+ {
+ if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
+ {
+ pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
+ break;
+ }
+ pNxt = pNxt->FindNext();
+ }
+ if ( pNxt )
+ {
+ const bool bMoveFwdInvalid = nullptr != GetIndNext();
+ const bool bNxtNew =
+ ( 0 == aRectFnSet.GetHeight(pNxt->getFramePrintArea()) ) &&
+ (!pNxt->IsTextFrame() ||!static_cast<SwTextFrame*>(pNxt)->IsHiddenNow());
+
+ pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+
+ if ( !bMovedBwd &&
+ ((bMoveFwdInvalid && !GetIndNext()) ||
+ bNxtNew) )
+ {
+ if( bMovedFwd )
+ oNotify->SetInvaKeep();
+ bMovedFwd = false;
+ }
+ }
+ }
+ continue;
+ }
+
+ // I don't fit into my parents, so it's time to make changes
+ // as constructively as possible.
+
+ //If I'm NOT allowed to leave the parent Frame, I've got a problem.
+ // Following Arthur Dent, we do the only thing that you can do with
+ // an unsolvable problem: We ignore it with all our power.
+ if ( !bMoveable || IsUndersized() )
+ {
+ break;
+ }
+
+ // If there's no way I can make myself fit into my Upper, the situation
+ // could still probably be mitigated by splitting up.
+ // This situation arises with freshly created Follows that had been moved
+ // to the next page but is still too big for it - ie. needs to be split
+ // as well.
+
+ // If I'm unable to split (WouldFit()) and can't be fitted, I'm going
+ // to tell my TextFrame part that, if possible, we still need to split despite
+ // the "don't split" attribute.
+ bool bMoveOrFit = false;
+ bool bDontMoveMe = !GetIndPrev();
+ if( bDontMoveMe && IsInSct() )
+ {
+ SwFootnoteBossFrame* pBoss = FindFootnoteBossFrame();
+ bDontMoveMe = !pBoss->IsInSct() ||
+ ( !pBoss->Lower()->GetNext() && !pBoss->GetPrev() );
+ }
+
+ // Finally, we are able to split table rows. Therefore, bDontMoveMe
+ // can be set to false:
+ if( bDontMoveMe && IsInTab() &&
+ nullptr != GetNextCellLeaf() )
+ bDontMoveMe = false;
+
+ assert(bMoveable);
+
+ if ( bDontMoveMe && aRectFnSet.GetHeight(getFrameArea()) >
+ aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) )
+ {
+ if ( !bFitPromise )
+ {
+ SwTwips nTmp = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) -
+ aRectFnSet.GetTop(getFramePrintArea());
+ bool bSplit = !IsFwdMoveAllowed();
+ if (nTmp > 0 && WouldFit(nTmp, bSplit, false, false))
+ {
+ Prepare( PrepareHint::WidowsOrphans, nullptr, false );
+ setFrameAreaSizeValid(false);
+ bFitPromise = true;
+ continue;
+ }
+ /*
+ * In earlier days, we never tried to fit TextFrames in
+ * frames and sections using bMoveOrFit by ignoring
+ * its attributes (Widows, Keep).
+ * This should have been done at least for column frames;
+ * as it must be tried anyway with linked frames and sections.
+ * Exception: If we sit in FormatWidthCols, we must not ignore
+ * the attributes.
+ */
+ else if ( !bFootnote &&
+ ( !bFly || !FindFlyFrame()->IsColLocked() ) &&
+ ( !bSct || !FindSctFrame()->IsColLocked() ) )
+ bMoveOrFit = true;
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL( "+TextFrame didn't respect WouldFit promise." );
+ }
+#endif
+ }
+
+ // Let's see if I can find some space somewhere...
+ // footnotes in the neighbourhood are moved into _MoveFootnoteCntFwd
+ SwFrame *pPre = GetIndPrev();
+ SwFrame *pOldUp = GetUpper();
+
+/* MA 13. Oct. 98: What is this supposed to be!?
+ * AMA 14. Dec 98: If a column section can't find any space for its first ContentFrame, it should be
+ * moved not only to the next column, but probably even to the next page, creating
+ * a section-follow there.
+ */
+ if( IsInSct() && bMovedFwd && bMakePage && pOldUp->IsColBodyFrame() &&
+ pOldUp->GetUpper()->GetUpper()->IsSctFrame() &&
+ ( pPre || pOldUp->GetUpper()->GetPrev() ) &&
+ static_cast<SwSectionFrame*>(pOldUp->GetUpper()->GetUpper())->MoveAllowed(this) )
+ {
+ bMovedFwd = false;
+ }
+
+ const bool bCheckForGrownBody = pOldUp->IsBodyFrame();
+ const tools::Long nOldBodyHeight = aRectFnSet.GetHeight(pOldUp->getFrameArea());
+
+ if ( !bMovedFwd && !MoveFwd( bMakePage, false ) )
+ bMakePage = false;
+ aRectFnSet.Refresh(this);
+ if (!bMovedFwd && bFootnote && GetIndPrev() != pPre)
+ { // SwFlowFrame::CutTree() could have formatted and deleted pPre
+ auto const pPrevFootnoteFrame(static_cast<SwFootnoteFrame const*>(
+ FindFootnoteFrame())->GetMaster());
+ bool bReset = true;
+ if (pPrevFootnoteFrame)
+ { // use GetIndNext() in case there are sections
+ for (auto p = pPrevFootnoteFrame->Lower(); p; p = p->GetIndNext())
+ {
+ if (p == pPre)
+ {
+ bReset = false;
+ break;
+ }
+ }
+ }
+ if (bReset)
+ {
+ pPre = nullptr;
+ }
+ }
+
+ // If MoveFwd moves the paragraph to the next page, a following
+ // paragraph, which contains footnotes can cause the old upper
+ // frame to grow. In this case we explicitly allow a new check
+ // for MoveBwd. Robust: We also check the bMovedBwd flag again.
+ // If pOldUp was a footnote frame, it has been deleted inside MoveFwd.
+ // Therefore we only check for growing body frames.
+ bMovedFwd = !bCheckForGrownBody || bMovedBwd || pOldUp == GetUpper() ||
+ aRectFnSet.GetHeight(pOldUp->getFrameArea()) <= nOldBodyHeight;
+
+ bFormatted = false;
+ if ( bMoveOrFit && GetUpper() == pOldUp )
+ {
+ // FME 2007-08-30 #i81146# new loop control
+ if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
+ {
+ Prepare( PrepareHint::MustFit, nullptr, false );
+ setFrameAreaSizeValid(false);
+ bMustFit = true;
+ continue;
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ OSL_FAIL( "LoopControl in SwContentFrame::MakeAll" );
+#endif
+ }
+ if ( bMovedBwd && GetUpper() )
+ { // Retire invalidations that have become useless.
+ GetUpper()->ResetCompletePaint();
+ if( pPre && !pPre->IsSctFrame() )
+ ::ValidateSz( pPre );
+ }
+
+ } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+
+ // NEW: Looping Louie (Light). Should not be applied in balanced sections.
+ // Should only be applied if there is no better solution!
+ LOOPING_LOUIE_LIGHT( bMovedFwd && bMovedBwd && !IsInBalancedSection() &&
+ (
+
+ ( bFootnote && !FindFootnoteFrame()->GetRef()->IsInSct() ) ||
+
+ // #i33887#
+ ( IsInSct() && bKeep )
+
+ // ... add your conditions here ...
+
+ ),
+ static_cast<SwTextFrame&>(*this) );
+
+ pSaveFootnote.reset();
+
+ UnlockJoin();
+ oDeleteGuard.reset();
+ if ( bMovedFwd || bMovedBwd )
+ oNotify->SetInvaKeep();
+ if ( bMovedFwd )
+ {
+ oNotify->SetInvalidatePrevPrtArea();
+ }
+ oNotify.reset();
+ SetFlyLock( false );
+}
+
+void MakeNxt( SwFrame *pFrame, SwFrame *pNxt )
+{
+ // fix(25455): Validate, otherwise this leads to a recursion.
+ // The first try, cancelling with pFrame = 0 if !Valid, leads to a problem, as
+ // the Keep may not be considered properly anymore (27417).
+ const bool bOldPos = pFrame->isFrameAreaPositionValid();
+ const bool bOldSz = pFrame->isFrameAreaSizeValid();
+ const bool bOldPrt = pFrame->isFramePrintAreaValid();
+ pFrame->setFrameAreaPositionValid(true);
+ pFrame->setFrameAreaSizeValid(true);
+ pFrame->setFramePrintAreaValid(true);
+
+ // fix(29272): Don't call MakeAll - there, pFrame might be invalidated again, and
+ // we recursively end up in here again.
+ if ( pNxt->IsContentFrame() )
+ {
+ SwContentNotify aNotify( static_cast<SwContentFrame*>(pNxt) );
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ if ( !pNxt->isFrameAreaSizeValid() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);
+
+ if( pNxt->IsVertical() )
+ {
+ aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
+ }
+ else
+ {
+ aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
+ }
+ }
+ static_cast<SwContentFrame*>(pNxt)->MakePrtArea( rAttrs );
+ pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
+ }
+ else
+ {
+ SwLayNotify aNotify( static_cast<SwLayoutFrame*>(pNxt) );
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ if ( !pNxt->isFrameAreaSizeValid() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);
+
+ if( pNxt->IsVertical() )
+ {
+ aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
+ }
+ else
+ {
+ aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
+ }
+ }
+ pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
+ }
+
+ pFrame->setFrameAreaPositionValid(bOldPos);
+ pFrame->setFrameAreaSizeValid(bOldSz);
+ pFrame->setFramePrintAreaValid(bOldPrt);
+}
+
+/// This routine checks whether there are no other FootnoteBosses
+/// between the pFrame's FootnoteBoss and the pNxt's FootnoteBoss.
+static bool lcl_IsNextFootnoteBoss( const SwFrame *pFrame, const SwFrame* pNxt )
+{
+ assert(pFrame && pNxt && "lcl_IsNextFootnoteBoss: No Frames?");
+ pFrame = pFrame->FindFootnoteBossFrame();
+ pNxt = pNxt->FindFootnoteBossFrame();
+ // If pFrame is a last column, we use the page instead.
+ while( pFrame && pFrame->IsColumnFrame() && !pFrame->GetNext() )
+ pFrame = pFrame->GetUpper()->FindFootnoteBossFrame();
+ // If pNxt is a first column, we use the page instead.
+ while( pNxt && pNxt->IsColumnFrame() && !pNxt->GetPrev() )
+ pNxt = pNxt->GetUpper()->FindFootnoteBossFrame();
+ // So... now pFrame and pNxt are either two adjacent pages or columns.
+ return pFrame && pNxt && pFrame->GetNext() == pNxt;
+}
+
+bool SwContentFrame::WouldFit_( SwTwips nSpace,
+ SwLayoutFrame *pNewUpper,
+ bool bTstMove,
+ const bool bObjsInNewUpper )
+{
+ // To have the footnote select its place carefully, it needs
+ // to be moved in any case if there is at least one page/column
+ // between the footnote and the new Upper.
+ SwFootnoteFrame* pFootnoteFrame = nullptr;
+ if ( IsInFootnote() )
+ {
+ if( !lcl_IsNextFootnoteBoss( pNewUpper, this ) )
+ return true;
+ pFootnoteFrame = FindFootnoteFrame();
+ }
+
+ bool bRet;
+ bool bSplit = !pNewUpper->Lower();
+ SwContentFrame *pFrame = this;
+ const SwFrame *pTmpPrev = pNewUpper->Lower();
+ if( pTmpPrev && pTmpPrev->IsFootnoteFrame() )
+ pTmpPrev = static_cast<const SwFootnoteFrame*>(pTmpPrev)->Lower();
+ while ( pTmpPrev && pTmpPrev->GetNext() )
+ pTmpPrev = pTmpPrev->GetNext();
+ do
+ {
+ // #i46181#
+ SwTwips nSecondCheck = 0;
+ SwTwips nOldSpace = nSpace;
+ bool bOldSplit = bSplit;
+
+ if ( bTstMove || IsInFly() || ( IsInSct() &&
+ ( pFrame->GetUpper()->IsColBodyFrame() || ( pFootnoteFrame &&
+ pFootnoteFrame->GetUpper()->GetUpper()->IsColumnFrame() ) ) ) )
+ {
+ // This is going to get a bit insidious now. If you're faint of heart,
+ // you'd better look away here. If a Fly contains columns, then the Contents
+ // are movable, except ones in the last column (see SwFrame::IsMoveable()).
+ // Of course they're allowed to float back. WouldFit() only returns a usable
+ // value if the Frame is movable. To fool WouldFit() into believing there's
+ // a movable Frame, I'm just going to hang it somewhere else for the time.
+ // The same procedure applies for column sections to make SwSectionFrame::Growable()
+ // return the proper value.
+ // Within footnotes, we may even need to put the SwFootnoteFrame somewhere else, if
+ // there's no SwFootnoteFrame there.
+ SwFrame* pTmpFrame = pFrame->IsInFootnote() && !pNewUpper->FindFootnoteFrame() ?
+ static_cast<SwFrame*>(pFrame->FindFootnoteFrame()) : pFrame;
+ SwLayoutFrame *pUp = pTmpFrame->GetUpper();
+ SwFrame *pOldNext = pTmpFrame->GetNext();
+ pTmpFrame->RemoveFromLayout();
+ pTmpFrame->InsertBefore( pNewUpper, nullptr );
+ // tdf#107126 for a section in a footnote, we have only inserted
+ // the SwTextFrame but no SwSectionFrame - reset mbInfSct flag
+ // to avoid crashing (but perhaps we should create a temp
+ // SwSectionFrame here because WidowsAndOrphans checks for that?)
+ pTmpFrame->InvalidateInfFlags();
+ if ( pFrame->IsTextFrame() &&
+ ( bTstMove ||
+ static_cast<SwTextFrame*>(pFrame)->HasFollow() ||
+ ( !static_cast<SwTextFrame*>(pFrame)->HasPara() &&
+ !static_cast<SwTextFrame*>(pFrame)->IsEmpty()
+ )
+ )
+ )
+ {
+ bTstMove = true;
+ bRet = static_cast<SwTextFrame*>(pFrame)->TestFormat( pTmpPrev, nSpace, bSplit );
+ }
+ else
+ bRet = pFrame->WouldFit(nSpace, bSplit, false, true);
+
+ pTmpFrame->RemoveFromLayout();
+ pTmpFrame->InsertBefore( pUp, pOldNext );
+ pTmpFrame->InvalidateInfFlags(); // restore flags
+ }
+ else
+ {
+ bRet = pFrame->WouldFit(nSpace, bSplit, false, true);
+ nSecondCheck = !bSplit ? 1 : 0;
+ }
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ // Sad but true: We need to consider the spacing in our calculation.
+ // This already happened in TestFormat.
+ if ( bRet && !bTstMove )
+ {
+ SwTwips nUpper;
+
+ if ( pTmpPrev )
+ {
+ nUpper = CalcUpperSpace( nullptr, pTmpPrev );
+
+ // in balanced columned section frames we do not want the
+ // common border
+ bool bCommonBorder = true;
+ if ( pFrame->IsInSct() && pFrame->GetUpper()->IsColBodyFrame() )
+ {
+ const SwSectionFrame* pSct = pFrame->FindSctFrame();
+ bCommonBorder = pSct->GetFormat()->GetBalancedColumns().GetValue();
+ }
+
+ // #i46181#
+ nSecondCheck = ( 1 == nSecondCheck &&
+ pFrame == this &&
+ IsTextFrame() &&
+ bCommonBorder &&
+ !static_cast<const SwTextFrame*>(this)->IsEmpty() ) ?
+ nUpper :
+ 0;
+
+ nUpper += bCommonBorder ?
+ rAttrs.GetBottomLine( *pFrame ) :
+ rAttrs.CalcBottomLine();
+
+ }
+ else
+ {
+ // #i46181#
+ nSecondCheck = 0;
+
+ if( pFrame->IsVertical() )
+ nUpper = pFrame->getFrameArea().Width() - pFrame->getFramePrintArea().Width();
+ else
+ nUpper = pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
+ }
+
+ nSpace -= nUpper;
+
+ if ( nSpace < 0 )
+ {
+ bRet = false;
+
+ // #i46181#
+ if ( nSecondCheck > 0 )
+ {
+ // The following code is intended to solve a (rare) problem
+ // causing some frames not to move backward:
+ // SwTextFrame::WouldFit() claims that the whole paragraph
+ // fits into the given space and subtracts the height of
+ // all lines from nSpace. nSpace - nUpper is not a valid
+ // indicator if the frame should be allowed to move backward.
+ // We do a second check with the original remaining space
+ // reduced by the required upper space:
+ nOldSpace -= nSecondCheck;
+ const bool bSecondRet = nOldSpace >= 0 && pFrame->WouldFit(nOldSpace, bOldSplit, false, true);
+ if ( bSecondRet && bOldSplit && nOldSpace >= 0 )
+ {
+ bRet = true;
+ bSplit = true;
+ }
+ }
+ }
+ }
+
+ // Also consider lower spacing in table cells
+ IDocumentSettingAccess const& rIDSA(pNewUpper->GetFormat()->getIDocumentSettingAccess());
+ if ( bRet && IsInTab() &&
+ rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
+ {
+ nSpace -= rAttrs.GetULSpace().GetLower();
+
+ if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
+ {
+ nSpace -= rAttrs.CalcLineSpacing();
+ }
+ if ( nSpace < 0 )
+ {
+ bRet = false;
+ }
+ }
+
+ if (bRet && !bSplit && pFrame->IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem()))
+ {
+ if( bTstMove )
+ {
+ while( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->HasFollow() )
+ {
+ pFrame = static_cast<SwTextFrame*>(pFrame)->GetFollow();
+ }
+ // If last follow frame of <this> text frame isn't valid,
+ // a formatting of the next content frame doesn't makes sense.
+ // Thus, return true.
+ if ( IsAnFollow( pFrame ) && !pFrame->isFrameAreaDefinitionValid() )
+ {
+ OSL_FAIL( "Only a warning for task 108824:/n<SwContentFrame::WouldFit_(..) - follow not valid!" );
+ return true;
+ }
+ }
+ SwFrame *pNxt;
+ if( nullptr != (pNxt = pFrame->FindNext()) && pNxt->IsContentFrame() &&
+ ( !pFootnoteFrame || ( pNxt->IsInFootnote() &&
+ pNxt->FindFootnoteFrame()->GetAttr() == pFootnoteFrame->GetAttr() ) ) )
+ {
+ // TestFormat(?) does not like paragraph- or character anchored objects.
+
+ // current solution for the test formatting doesn't work, if
+ // objects are present in the remaining area of the new upper
+ if ( bTstMove &&
+ ( pNxt->GetDrawObjs() || bObjsInNewUpper ) )
+ {
+ return true;
+ }
+
+ if ( !pNxt->isFrameAreaDefinitionValid() )
+ {
+ MakeNxt( pFrame, pNxt );
+ }
+
+ // Little trick: if the next has a predecessor, then the paragraph
+ // spacing has been calculated already, and we don't need to re-calculate
+ // it in an expensive way.
+ if( lcl_NotHiddenPrev( pNxt ) )
+ pTmpPrev = nullptr;
+ else
+ {
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow() )
+ pTmpPrev = lcl_NotHiddenPrev( pFrame );
+ else
+ pTmpPrev = pFrame;
+ }
+ pFrame = static_cast<SwContentFrame*>(pNxt);
+ }
+ else
+ pFrame = nullptr;
+ }
+ else
+ pFrame = nullptr;
+
+ } while ( bRet && pFrame );
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/colfrm.cxx b/sw/source/core/layout/colfrm.cxx
new file mode 100644
index 000000000..4b13576a7
--- /dev/null
+++ b/sw/source/core/layout/colfrm.cxx
@@ -0,0 +1,446 @@
+/* -*- 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 <editeng/ulspitem.hxx>
+#include <osl/diagnose.h>
+#include <fmtclds.hxx>
+#include <fmtfordr.hxx>
+#include <frmfmt.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <colfrm.hxx>
+#include <pagefrm.hxx>
+#include <bodyfrm.hxx>
+#include <rootfrm.hxx>
+#include <sectfrm.hxx>
+#include <calbck.hxx>
+#include <ftnfrm.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+
+SwColumnFrame::SwColumnFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
+ SwFootnoteBossFrame( pFormat, pSib )
+{
+ mnFrameType = SwFrameType::Column;
+ SwBodyFrame* pColBody = new SwBodyFrame( pFormat->GetDoc()->GetDfltFrameFormat(), pSib );
+ pColBody->InsertBehind( this, nullptr ); // ColumnFrames now with BodyFrame
+ SetMaxFootnoteHeight( LONG_MAX );
+}
+
+void SwColumnFrame::DestroyImpl()
+{
+ SwFrameFormat *pFormat = GetFormat();
+ SwDoc *pDoc;
+ if ( !(pDoc = pFormat->GetDoc())->IsInDtor() && pFormat->HasOnlyOneListener() )
+ {
+ //I'm the only one, delete the format.
+ //Get default format before, so the base class can cope with it.
+ pDoc->GetDfltFrameFormat()->Add( this );
+ // tdf#134009, like #i32968# avoid SwUndoFrameFormatDelete creation,
+ // the format is owned by the SwColumnFrame, see lcl_AddColumns()
+ ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
+ pDoc->DelFrameFormat( pFormat );
+ }
+
+ SwFootnoteBossFrame::DestroyImpl();
+}
+
+SwColumnFrame::~SwColumnFrame()
+{
+}
+
+static void lcl_RemoveColumns( SwLayoutFrame *pCont, sal_uInt16 nCnt )
+{
+ OSL_ENSURE( pCont && pCont->Lower() && pCont->Lower()->IsColumnFrame(),
+ "no columns to remove." );
+
+ SwColumnFrame *pColumn = static_cast<SwColumnFrame*>(pCont->Lower());
+ sw_RemoveFootnotes( pColumn, true, true );
+ while ( pColumn->GetNext() )
+ {
+ OSL_ENSURE( pColumn->GetNext()->IsColumnFrame(),
+ "neighbor of ColumnFrame is no ColumnFrame." );
+ pColumn = static_cast<SwColumnFrame*>(pColumn->GetNext());
+ }
+ for ( sal_uInt16 i = 0; i < nCnt; ++i )
+ {
+ SwColumnFrame *pTmp = static_cast<SwColumnFrame*>(pColumn->GetPrev());
+ pColumn->Cut();
+ SwFrame::DestroyFrame(pColumn); //format is going to be destroyed in the DTor if needed.
+ pColumn = pTmp;
+ }
+}
+
+static SwLayoutFrame * lcl_FindColumns( SwLayoutFrame *pLay, sal_uInt16 nCount )
+{
+ SwFrame *pCol = pLay->Lower();
+ if ( pLay->IsPageFrame() )
+ pCol = static_cast<SwPageFrame*>(pLay)->FindBodyCont()->Lower();
+
+ if ( pCol && pCol->IsColumnFrame() )
+ {
+ SwFrame *pTmp = pCol;
+ sal_uInt16 i;
+ for ( i = 0; pTmp; pTmp = pTmp->GetNext(), ++i )
+ /* do nothing */;
+ return i == nCount ? static_cast<SwLayoutFrame*>(pCol) : nullptr;
+ }
+ return nullptr;
+}
+
+static bool lcl_AddColumns( SwLayoutFrame *pCont, sal_uInt16 nCount )
+{
+ SwDoc *pDoc = pCont->GetFormat()->GetDoc();
+ const bool bMod = pDoc->getIDocumentState().IsModified();
+
+ //Formats should be shared whenever possible. If a neighbour already has
+ //the same column settings we can add them to the same format.
+ //The neighbour can be searched using the format, however the owner of the
+ //attribute depends on the frame type.
+ SwLayoutFrame *pAttrOwner = pCont;
+ if ( pCont->IsBodyFrame() )
+ pAttrOwner = pCont->FindPageFrame();
+ SwLayoutFrame *pNeighbourCol = nullptr;
+ SwIterator<SwLayoutFrame,SwFormat> aIter( *pAttrOwner->GetFormat() );
+ SwLayoutFrame *pNeighbour = aIter.First();
+
+ sal_uInt16 nAdd = 0;
+ SwFrame *pCol = pCont->Lower();
+ if ( pCol && pCol->IsColumnFrame() )
+ for ( nAdd = 1; pCol; pCol = pCol->GetNext(), ++nAdd )
+ /* do nothing */;
+ while ( pNeighbour )
+ {
+ if ( nullptr != (pNeighbourCol = lcl_FindColumns( pNeighbour, nCount+nAdd )) &&
+ pNeighbourCol != pCont )
+ break;
+ pNeighbourCol = nullptr;
+ pNeighbour = aIter.Next();
+ }
+
+ bool bRet;
+ SwTwips nMax = pCont->IsPageBodyFrame() ?
+ pCont->FindPageFrame()->GetMaxFootnoteHeight() : LONG_MAX;
+ if ( pNeighbourCol )
+ {
+ bRet = false;
+ SwFrame *pTmp = pCont->Lower();
+ while ( pTmp )
+ {
+ pTmp = pTmp->GetNext();
+ pNeighbourCol = static_cast<SwLayoutFrame*>(pNeighbourCol->GetNext());
+ }
+ for ( sal_uInt16 i = 0; i < nCount; ++i )
+ {
+ SwColumnFrame *pTmpCol = new SwColumnFrame( pNeighbourCol->GetFormat(), pCont );
+ pTmpCol->SetMaxFootnoteHeight( nMax );
+ pTmpCol->InsertBefore( pCont, nullptr );
+ pNeighbourCol = static_cast<SwLayoutFrame*>(pNeighbourCol->GetNext());
+ }
+ }
+ else
+ {
+ bRet = true;
+ // tdf#103359, like #i32968# Inserting columns in the section causes MakeFrameFormat to put
+ // nCount objects of type SwUndoFrameFormat on the undo stack. We don't want them.
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+ for ( sal_uInt16 i = 0; i < nCount; ++i )
+ {
+ SwFrameFormat *pFormat = pDoc->MakeFrameFormat(OUString(), pDoc->GetDfltFrameFormat());
+ SwColumnFrame *pTmp = new SwColumnFrame( pFormat, pCont );
+ pTmp->SetMaxFootnoteHeight( nMax );
+ pTmp->Paste( pCont );
+ }
+ }
+
+ if ( !bMod )
+ pDoc->getIDocumentState().ResetModified();
+ return bRet;
+}
+
+/** add or remove columns from a layoutframe.
+ *
+ * Normally, a layoutframe with a column attribute of 1 or 0 columns contains
+ * no columnframe. However, a sectionframe with "footnotes at the end" needs
+ * a columnframe.
+ *
+ * @param rOld
+ * @param rNew
+ * @param bChgFootnote if true, the columnframe will be inserted or removed, if necessary.
+ */
+void SwLayoutFrame::ChgColumns( const SwFormatCol &rOld, const SwFormatCol &rNew,
+ const bool bChgFootnote )
+{
+ if ( rOld.GetNumCols() <= 1 && rNew.GetNumCols() <= 1 && !bChgFootnote )
+ return;
+ // #i97379#
+ // If current lower is a no text frame, then columns are not allowed
+ if ( Lower() && Lower()->IsNoTextFrame() &&
+ rNew.GetNumCols() > 1 )
+ {
+ return;
+ }
+
+ sal_uInt16 nNewNum, nOldNum = 1;
+ if( Lower() && Lower()->IsColumnFrame() )
+ {
+ SwFrame* pCol = Lower();
+ while( nullptr != (pCol=pCol->GetNext()) )
+ ++nOldNum;
+ }
+ nNewNum = rNew.GetNumCols();
+ if( !nNewNum )
+ ++nNewNum;
+ bool bAtEnd;
+ if( IsSctFrame() )
+ bAtEnd = static_cast<SwSectionFrame*>(this)->IsAnyNoteAtEnd();
+ else
+ bAtEnd = false;
+
+ //Setting the column width is only needed for new formats.
+ bool bAdjustAttributes = nOldNum != rOld.GetNumCols();
+
+ //The content is saved and restored if the column count is different.
+ SwFrame *pSave = nullptr;
+ if( nOldNum != nNewNum || bChgFootnote )
+ {
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ OSL_ENSURE( pDoc, "FrameFormat doesn't return a document." );
+ // SaveContent would also suck up the content of the footnote container
+ // and store it within the normal text flow.
+ if( IsPageBodyFrame() )
+ pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->RemoveFootnotes( static_cast<SwPageFrame*>(GetUpper()) );
+ pSave = ::SaveContent( this );
+
+ //If columns exist, they get deleted if a column count of 0 or 1 is requested.
+ if ( nNewNum == 1 && !bAtEnd )
+ {
+ ::lcl_RemoveColumns( this, nOldNum );
+ if ( IsBodyFrame() )
+ SetFrameFormat( pDoc->GetDfltFrameFormat() );
+ else
+ GetFormat()->SetFormatAttr( SwFormatFillOrder() );
+ if ( pSave )
+ ::RestoreContent( pSave, this, nullptr );
+ return;
+ }
+ if ( nOldNum == 1 )
+ {
+ if ( IsBodyFrame() )
+ SetFrameFormat( pDoc->GetColumnContFormat() );
+ else
+ GetFormat()->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ) );
+ if( !Lower() || !Lower()->IsColumnFrame() )
+ --nOldNum;
+ }
+ if ( nOldNum > nNewNum )
+ {
+ ::lcl_RemoveColumns( this, nOldNum - nNewNum );
+ bAdjustAttributes = true;
+ }
+ else if( nOldNum < nNewNum )
+ {
+ sal_uInt16 nAdd = nNewNum - nOldNum;
+ bAdjustAttributes = lcl_AddColumns( this, nAdd );
+ }
+ }
+
+ if ( !bAdjustAttributes )
+ {
+ if ( rOld.GetLineWidth() != rNew.GetLineWidth() ||
+ rOld.GetWishWidth() != rNew.GetWishWidth() ||
+ rOld.IsOrtho() != rNew.IsOrtho() )
+ bAdjustAttributes = true;
+ else
+ {
+ const size_t nCount = std::min( rNew.GetColumns().size(), rOld.GetColumns().size() );
+ for ( size_t i = 0; i < nCount; ++i )
+ if ( !(rOld.GetColumns()[i] == rNew.GetColumns()[i]) )
+ {
+ bAdjustAttributes = true;
+ break;
+ }
+ }
+ }
+
+ //The columns can now be easily adjusted.
+ AdjustColumns( &rNew, bAdjustAttributes );
+
+ //Don't restore the content before. An earlier restore would trigger useless
+ //actions during setup.
+ if ( pSave )
+ {
+ OSL_ENSURE( Lower() && Lower()->IsLayoutFrame() &&
+ static_cast<SwLayoutFrame*>(Lower())->Lower() &&
+ static_cast<SwLayoutFrame*>(Lower())->Lower()->IsLayoutFrame(),
+ "no column body." ); // ColumnFrames contain BodyFrames
+ ::RestoreContent( pSave, static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(Lower())->Lower()), nullptr );
+ }
+}
+
+void SwLayoutFrame::AdjustColumns( const SwFormatCol *pAttr, bool bAdjustAttributes )
+{
+ if( !Lower()->GetNext() )
+ {
+ Lower()->ChgSize( getFramePrintArea().SSize() );
+ return;
+ }
+
+ const bool bVert = IsVertical();
+
+ SwRectFn fnRect = bVert ? ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+
+ //If we have a pointer or we have to configure an attribute, we set the
+ //column widths in any case. Otherwise we check if a configuration is needed.
+ if ( !pAttr )
+ {
+ pAttr = &GetFormat()->GetCol();
+ if ( !bAdjustAttributes )
+ {
+ tools::Long nAvail = (getFramePrintArea().*fnRect->fnGetWidth)();
+ for ( SwLayoutFrame *pCol = static_cast<SwLayoutFrame*>(Lower());
+ pCol;
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext()) )
+ nAvail -= (pCol->getFrameArea().*fnRect->fnGetWidth)();
+ if ( !nAvail )
+ return;
+ }
+ }
+
+ //The columns can now be easily adjusted.
+ //The widths get counted so we can give the reminder to the last one.
+ SwTwips nAvail = (getFramePrintArea().*fnRect->fnGetWidth)();
+ const bool bLine = pAttr->GetLineAdj() != COLADJ_NONE;
+ const sal_uInt16 nMin = bLine ? sal_uInt16( 20 + ( pAttr->GetLineWidth() / 2) ) : 0;
+
+ const bool bR2L = IsRightToLeft();
+ SwFrame *pCol = bR2L ? GetLastLower() : Lower();
+
+ // #i27399#
+ // bOrtho means we have to adjust the column frames manually. Otherwise
+ // we may use the values returned by CalcColWidth:
+ const bool bOrtho = pAttr->IsOrtho() && pAttr->GetNumCols() > 0;
+ tools::Long nGutter = 0;
+
+ for ( sal_uInt16 i = 0; i < pAttr->GetNumCols() && pCol; ++i ) //i118878, value returned by GetNumCols() can't be trusted
+ {
+ if( !bOrtho )
+ {
+ const SwTwips nWidth = i == (pAttr->GetNumCols() - 1) ?
+ nAvail :
+ pAttr->CalcColWidth( i, sal_uInt16( (getFramePrintArea().*fnRect->fnGetWidth)() ) );
+
+ const Size aColSz = bVert ?
+ Size( getFramePrintArea().Width(), nWidth ) :
+ Size( nWidth, getFramePrintArea().Height() );
+
+ pCol->ChgSize( aColSz );
+
+ // With this, the ColumnBodyFrames from page columns gets adjusted and
+ // their bFixHeight flag is set so they won't shrink/grow.
+ // Don't use the flag with frame columns because BodyFrames in frame
+ // columns can grow/shrink.
+ if( IsBodyFrame() )
+ static_cast<SwLayoutFrame*>(pCol)->Lower()->ChgSize( aColSz );
+
+ nAvail -= nWidth;
+ }
+
+ if ( bOrtho || bAdjustAttributes )
+ {
+ const SwColumn *pC = &pAttr->GetColumns()[i];
+ const SwAttrSet* pSet = pCol->GetAttrSet();
+ SvxLRSpaceItem aLR( pSet->GetLRSpace() );
+
+ //In order to have enough space for the separation lines, we have to
+ //take them into account here. Every time two columns meet we
+ //calculate a clearance of 20 + half the pen width on the left or
+ //right side, respectively.
+ const sal_uInt16 nLeft = pC->GetLeft();
+ const sal_uInt16 nRight = pC->GetRight();
+
+ aLR.SetLeft ( nLeft );
+ aLR.SetRight( nRight );
+
+ if ( bLine )
+ {
+ if ( i == 0 )
+ {
+ aLR.SetRight( std::max( nRight, nMin ) );
+ }
+ else if ( i == pAttr->GetNumCols() - 1 )
+ {
+ aLR.SetLeft ( std::max( nLeft, nMin ) );
+ }
+ else
+ {
+ aLR.SetLeft ( std::max( nLeft, nMin ) );
+ aLR.SetRight( std::max( nRight, nMin ) );
+ }
+ }
+
+ if ( bAdjustAttributes )
+ {
+ SvxULSpaceItem aUL( pSet->GetULSpace() );
+ aUL.SetUpper(0);
+ aUL.SetLower(0);
+
+ static_cast<SwLayoutFrame*>(pCol)->GetFormat()->SetFormatAttr( aLR );
+ static_cast<SwLayoutFrame*>(pCol)->GetFormat()->SetFormatAttr( aUL );
+ }
+
+ nGutter += aLR.GetLeft() + aLR.GetRight();
+ }
+
+ pCol = bR2L ? pCol->GetPrev() : pCol->GetNext();
+ }
+
+ if( !bOrtho )
+ return;
+
+ tools::Long nInnerWidth = ( nAvail - nGutter ) / pAttr->GetNumCols();
+ pCol = Lower();
+ for( sal_uInt16 i = 0; i < pAttr->GetNumCols() && pCol; pCol = pCol->GetNext(), ++i ) //i118878, value returned by GetNumCols() can't be trusted
+ {
+ SwTwips nWidth;
+ if ( i == pAttr->GetNumCols() - 1 )
+ nWidth = nAvail;
+ else
+ {
+ SvxLRSpaceItem aLR( pCol->GetAttrSet()->GetLRSpace() );
+ nWidth = nInnerWidth + aLR.GetLeft() + aLR.GetRight();
+ }
+ if( nWidth < 0 )
+ nWidth = 0;
+
+ const Size aColSz = bVert ?
+ Size( getFramePrintArea().Width(), nWidth ) :
+ Size( nWidth, getFramePrintArea().Height() );
+
+ pCol->ChgSize( aColSz );
+
+ if( IsBodyFrame() )
+ static_cast<SwLayoutFrame*>(pCol)->Lower()->ChgSize( aColSz );
+
+ nAvail -= nWidth;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/dbg_lay.cxx b/sw/source/core/layout/dbg_lay.cxx
new file mode 100644
index 000000000..d1d012c64
--- /dev/null
+++ b/sw/source/core/layout/dbg_lay.cxx
@@ -0,0 +1,953 @@
+/* -*- 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 .
+ */
+
+#ifdef DBG_UTIL
+
+/*
+ * And here's the description:
+ *
+ * The PROTOCOL macros allow you to log events in frame methods. In places where
+ * logging is useful either one of the PROTOCOL(...) or PROTOCOL_ENTER(...) can
+ * be used. PROTOCOL_ENTER(...) additionally logs the leaving of a method.
+ *
+ * The PROTOCOL macros accept the following parameters:
+ * 1. A pointer to an SwFrame (usually "this" or "rThis")
+ * 2. The function group i.e. PROT::MakeAll. This is used to decide (inline)
+ * whether this event shall be logged at the current time.
+ * 3. The action, usually 0. For example DbgAction::Start indents output in the log
+ * file and DbgAction::End stops the indentation. This allows for example
+ * PROTOCOL_ENTER to indent at the beginning of a method and stop indenting
+ * when leaving the method.
+ * 4. The fourth parameter is a void pointer which allows to pass anything
+ * which can be used in the log. A good example is PROT::Grow: this requires
+ * a pointer to the value which defines how much to grow.
+ *
+ * The log file is called "dbg_lay.out", which is saved in the current (BIN-)
+ * directory. The file contains lines with FrameId, function group and additional
+ * information.
+ *
+ * What exactly is going to be logged, can be defined as follows:
+ * 1. The static variable SwProtocol::nRecord contains the function groups
+ * which shall be logged.
+ * A value of i.e. PROT::Grow causes calls to SwFrame::Grow to be
+ * logged; PROT::MakeAll logs the calls to xxx::MakeAll.
+ * The PROT_XY values can be combined using binary OR, the default value
+ * is null - no method calls are logged.
+ * 2. The SwImplProtocol class contains a filter for frame types, only method
+ * call of frame types which are defined there are logged.
+ * The member nTypes can be set to values like SwFrameType::Page or SwFrameType::Section and
+ * may be combined using binary OR. The default values is 0xFFFF - meaning
+ * all frame types.
+ * 3. The SwImplProtocol class contains an ArrayPointer to FrameIds which need to be
+ * tracked. If the pointer is null, all frames will be logged; otherwise
+ * only frames of linked from the array will be logged.
+ *
+ * Code changes are needed to start logging; either change the default of nRecord
+ * in SwProtocol::Init() or change the debugger. There are several possible
+ * places in the debugger:
+ * 1. Set a breakpoint in SwProtocol::Init() and manipulate nRecord there, set
+ * FrameIds accordingly then start logging during program start.
+ * 2. Set a breakpoint before any PROTOCOL or PROTOCOL_ENTER macro during
+ * program execution, then set the lowest bit (PROT::Init) of
+ * SwProtocol::nRecord. This activates the function group of the following
+ * macro and causes it to be logged in the future.
+ * 3. There's a special case for 2: If one uses 2. in SwRootFrame::PaintSwFrame(..),
+ * the log settings are taken from the file "dbg_lay.ini"!
+ * In this INI-file you can have comment lines starting with a '#'.
+ * The sections "[frmid]", "[frmtype]" and "[record]" are relevant.
+ * In the [frmid] section, you can put FrameIds of the Frames to be logged.
+ * If there are no entries in this section, all Frames will be logged.
+ * In the [frmtype] section, the frame types which should be logged are
+ * listed; default is USHRT_MAX which means that all types are logged.
+ * It's possible to remove types from the list using '!' in front of a
+ * value. The value !0xC000 would for example exclude SwContentFrames from
+ * logging.
+ * In the [record] section the functions group which should be logged are
+ * listed; default is 0 which means that none are logged. It's also
+ * possible to remove functions using '!'.
+ * An example INI file:
+ * #Functions: all(0x0007ffff), except PrintArea (0x200)
+ * [record] 524287 !512
+ * [frmid]
+ * #the following FrameIds:
+ * 1 2 12 13 14 15
+ * #no layout frames, except ColumnFrames
+ * [frmtype] !0x3FFF 0x4
+ *
+ * As soon as the logging is in process, one can manipulate many things in
+ * SwImplProtocol::Record_(...) using a debugger, especially concerning
+ * frame types and FrameIds.
+ */
+
+#include <dbg_lay.hxx>
+
+#include <txtfrm.hxx>
+#include <fntcache.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <layfrm.hxx>
+#include <frame.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+
+PROT SwProtocol::s_nRecord = PROT::FileInit;
+SwImplProtocol* SwProtocol::s_pImpl = nullptr;
+
+static sal_uLong lcl_GetFrameId( const SwFrame* pFrame )
+{
+#if OSL_DEBUG_LEVEL > 1
+ static bool bFrameId = false;
+ if( bFrameId )
+ return pFrame->GetFrameId();
+#endif
+ if( pFrame )
+ return pFrame->GetFrameId();
+ return 0;
+}
+
+class SwImplProtocol
+{
+ std::unique_ptr<SvFileStream> m_pStream; // output stream
+ std::unique_ptr<std::set<sal_uInt16>>
+ m_pFrameIds; // which FrameIds shall be logged ( NULL == all)
+ std::vector<tools::Long> m_aVars; // variables
+ OStringBuffer m_aLayer; // indentation of output (" " per start/end)
+ SwFrameType m_nTypes; // which types shall be logged
+ sal_uInt16 m_nLineCount; // printed lines
+ sal_uInt16 m_nMaxLines; // max lines to be printed
+ sal_uInt8 m_nInitFile; // range (FrameId,FrameType,Record) during reading of the INI file
+ sal_uInt8
+ m_nTestMode; // special for test formatting, logging may only be done in test formatting.
+ void Record_( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam );
+ bool NewStream();
+ void CheckLine( OString& rLine );
+ static void SectFunc( OStringBuffer& rOut, DbgAction nAct, void const * pParam );
+public:
+ SwImplProtocol();
+ ~SwImplProtocol();
+ // logging
+ void Record( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam )
+ {
+ if (m_pStream)
+ Record_(pFrame, nFunction, nAct, pParam);
+ }
+ void InsertFrame( sal_uInt16 nFrameId ); // take FrameId for logging
+ void DeleteFrame( sal_uInt16 nFrameId ); // remove FrameId; don't log him anymore
+ void FileInit(); // read the INI file
+ void ChkStream() {
+ if (!m_pStream)
+ NewStream();
+ }
+};
+
+/* Through the PROTOCOL_ENTER macro a SwEnterLeave object gets created. If the
+ * current function should be logged as SwImplEnterLeace object gets created.
+ * The funny thing here is, that the Ctor of the Impl object is automatically
+ * called at the beginning of the function and the Dtor is automatically called
+ * when leaving the function. In the base implementation the Ctor calls only
+ * PROTOCOL(..) with DbgAction::Start and in the Dtor a PROTOCOL(..) with DbgAction::End.
+ * It's possible to derive from this class, for example to be able to document
+ * frame resize while leaving a function. To do this, one only needs to add the
+ * desired SwImplEnterLeave class in SwEnterLeave::Ctor().
+ */
+
+class SwImplEnterLeave
+{
+protected:
+ const SwFrame* m_pFrame; // the frame
+ PROT m_nFunction; // the function
+ DbgAction m_nAction; // the action if needed
+ void* m_pParam; // further parameter
+public:
+ SwImplEnterLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
+ : m_pFrame(pF)
+ , m_nFunction(nFunct)
+ , m_nAction(nAct)
+ , m_pParam(pPar)
+ {
+ }
+ virtual ~SwImplEnterLeave() {}
+ virtual void Enter(); // message when entering
+ virtual void Leave(); // message when leaving
+};
+
+namespace {
+
+class SwSizeEnterLeave : public SwImplEnterLeave
+{
+ tools::Long m_nFrameHeight;
+
+public:
+ SwSizeEnterLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
+ : SwImplEnterLeave(pF, nFunct, nAct, pPar)
+ , m_nFrameHeight(pF->getFrameArea().Height())
+ {
+ }
+
+ virtual void Leave() override; // resize message
+};
+
+class SwUpperEnterLeave : public SwImplEnterLeave
+{
+ sal_uInt16 m_nFrameId;
+
+public:
+ SwUpperEnterLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
+ : SwImplEnterLeave(pF, nFunct, nAct, pPar)
+ , m_nFrameId(0)
+ {
+ }
+
+ virtual void Enter() override; // message
+ virtual void Leave() override; // message of FrameId from upper
+};
+
+class SwFrameChangesLeave : public SwImplEnterLeave
+{
+ SwRect m_aFrame;
+
+public:
+ SwFrameChangesLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
+ : SwImplEnterLeave(pF, nFunct, nAct, pPar)
+ , m_aFrame(pF->getFrameArea())
+ {
+ }
+
+ virtual void Enter() override; // no message
+ virtual void Leave() override; // message when resizing the Frame area
+};
+
+}
+
+void SwProtocol::Record( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam )
+{
+ if( Start() )
+ { // We reach this point if SwProtocol::nRecord is binary OR'd with PROT::Init(0x1) using the debugger
+ bool bFinit = false; // This gives the possibility to stop logging of this action in the debugger
+ if( bFinit )
+ {
+ s_nRecord &= ~nFunction; // Don't log this function any longer
+ s_nRecord &= ~PROT::Init; // Always reset PROT::Init
+ return;
+ }
+ s_nRecord |= nFunction; // Activate logging of this function
+ s_nRecord &= ~PROT::Init; // Always reset PROT::Init
+ if( s_pImpl )
+ s_pImpl->ChkStream();
+ }
+ if( !s_pImpl ) // Create Impl object if needed
+ s_pImpl = new SwImplProtocol();
+ s_pImpl->Record( pFrame, nFunction, nAct, pParam ); // ...and start logging
+}
+
+// The following function gets called when pulling in the writer DLL through
+// TextInit(..) and gives the possibility to release functions
+// and/or FrameIds to the debugger
+
+void SwProtocol::Init()
+{
+ s_nRecord = PROT::FileInit;
+ SvFileStream aStream( "dbg_lay.go", StreamMode::READ );
+ if( aStream.IsOpen() )
+ {
+ s_pImpl = new SwImplProtocol();
+ s_pImpl->FileInit();
+ }
+ aStream.Close();
+}
+
+// End of logging
+
+void SwProtocol::Stop()
+{
+ if( s_pImpl )
+ {
+ delete s_pImpl;
+ s_pImpl = nullptr;
+ if( pFntCache )
+ pFntCache->Flush();
+ }
+ s_nRecord = PROT::FileInit;
+}
+
+SwImplProtocol::SwImplProtocol()
+ : m_nTypes(FRM_ALL)
+ , m_nLineCount(0)
+ , m_nMaxLines(USHRT_MAX)
+ , m_nTestMode(0)
+{
+ NewStream();
+}
+
+bool SwImplProtocol::NewStream()
+{
+ m_nLineCount = 0;
+ m_pStream.reset(new SvFileStream("dbg_lay.out", StreamMode::WRITE | StreamMode::TRUNC));
+ if (m_pStream->GetError())
+ {
+ m_pStream.reset();
+ }
+ return nullptr != m_pStream;
+}
+
+SwImplProtocol::~SwImplProtocol()
+{
+ if (m_pStream)
+ {
+ m_pStream->Close();
+ m_pStream.reset();
+ }
+ m_pFrameIds.reset();
+ m_aVars.clear();
+}
+
+/// analyze a line in the INI file
+void SwImplProtocol::CheckLine( OString& rLine )
+{
+ rLine = rLine.toAsciiLowerCase(); // upper/lower case is the same
+ rLine = rLine.replace( '\t', ' ' );
+ if( '#' == rLine[0] ) // comments start with '#'
+ return;
+ if( '[' == rLine[0] ) // section: FrameIds, type or function
+ {
+ std::string_view aTmp = o3tl::getToken(rLine, 0, ']');
+ if (aTmp == "[frmid") // section FrameIds
+ {
+ m_nInitFile = 1;
+ m_pFrameIds.reset(); // default: log all frames
+ }
+ else if (aTmp == "[frmtype")// section types
+ {
+ m_nInitFile = 2;
+ m_nTypes = FRM_ALL; // default: log all frame types
+ }
+ else if (aTmp == "[record")// section functions
+ {
+ m_nInitFile = 3;
+ SwProtocol::SetRecord( PROT::FileInit );// default: don't log any function
+ }
+ else if (aTmp == "[test")// section functions
+ {
+ m_nInitFile = 4; // default:
+ m_nTestMode = 0; // log outside of test formatting
+ }
+ else if (aTmp == "[max")// Max number of lines
+ {
+ m_nInitFile = 5; // default:
+ m_nMaxLines = USHRT_MAX;
+ }
+ else if (aTmp == "[var")// variables
+ {
+ m_nInitFile = 6;
+ }
+ else
+ m_nInitFile = 0; // oops: unknown section?
+ rLine = rLine.copy(aTmp.size() + 1);
+ }
+
+ // spaces (or tabs) are the delimiter
+ sal_Int32 nIndex = 0;
+ do
+ {
+ std::string_view aTok = o3tl::getToken(rLine, 0, ' ', nIndex );
+ bool bNo = false;
+ if( !aTok.empty() && '!' == aTok[0] )
+ {
+ bNo = true; // remove this function/type
+ aTok = aTok.substr(1);
+ }
+ if( !aTok.empty() )
+ {
+ sal_Int64 nVal = o3tl::toInt64(aTok);
+ switch (m_nInitFile)
+ {
+ case 1: InsertFrame( sal_uInt16( nVal ) ); // add FrameId
+ break;
+ case 2: {
+ SwFrameType nNew = static_cast<SwFrameType>(nVal);
+ if( bNo )
+ m_nTypes &= ~nNew; // remove type
+ else
+ m_nTypes |= nNew; // add type
+ }
+ break;
+ case 3: {
+ PROT nOld = SwProtocol::Record();
+ if( bNo )
+ nOld &= ~PROT(nVal & o3tl::typed_flags<PROT>::mask); // remove function
+ else
+ nOld |= PROT(nVal & o3tl::typed_flags<PROT>::mask); // remove function
+ SwProtocol::SetRecord( nOld );
+ }
+ break;
+ case 4: {
+ sal_uInt8 nNew = static_cast<sal_uInt8>(nVal);
+ if( bNo )
+ m_nTestMode &= ~nNew; // reset test mode
+ else
+ m_nTestMode |= nNew; // set test mode
+ }
+ break;
+ case 5:
+ m_nMaxLines = o3tl::narrowing<sal_uInt16>(nVal);
+ break;
+ case 6:
+ m_aVars.push_back(nVal);
+ break;
+ }
+ }
+ }
+ while ( nIndex >= 0 );
+}
+
+/// read the file "dbg_lay.ini" in the current directory and evaluate it.
+void SwImplProtocol::FileInit()
+{
+ SvFileStream aStream( "dbg_lay.ini", StreamMode::READ );
+ if( aStream.IsOpen() )
+ {
+ OString aLine;
+ m_nInitFile = 0;
+ while( aStream.good() )
+ {
+ char c;
+ aStream.ReadChar( c );
+ if( '\n' == c || '\r' == c ) // line ending
+ {
+ aLine = aLine.trim();
+ if( !aLine.isEmpty() )
+ CheckLine( aLine ); // evaluate line
+ aLine.clear();
+ }
+ else
+ aLine += OStringChar(c);
+ }
+ if( !aLine.isEmpty() )
+ CheckLine( aLine ); // evaluate last line
+ }
+ aStream.Close();
+}
+
+/// enable indentation by two spaces during DbgAction::Start and disable it again at DbgAction::End.
+static void lcl_Start(OStringBuffer& rOut, OStringBuffer& rLay, DbgAction nAction)
+{
+ if( nAction == DbgAction::Start )
+ {
+ rLay.append(" ");
+ rOut.append(" On");
+ }
+ else if( nAction == DbgAction::End )
+ {
+ if( rLay.getLength() > 1 )
+ {
+ rLay.remove(rLay.getLength() - 2, rLay.getLength());
+ rOut.remove(0, 2);
+ }
+ rOut.append(" Off");
+ }
+}
+
+/// output the ValidSize-, ValidPos- and ValidPrtArea-Flag ("Sz","Ps","PA")
+/// of the frame; "+" stands for valid, "-" stands for invalid.
+static void lcl_Flags(OStringBuffer& rOut, const SwFrame* pFrame)
+{
+ rOut.append(" ValidSize");
+ rOut.append(pFrame->isFrameAreaSizeValid() ? '+' : '-');
+ rOut.append(" ValidPos");
+ rOut.append(pFrame->isFrameAreaPositionValid() ? '+' : '-');
+ rOut.append(" ValidPrtArea");
+ rOut.append(pFrame->isFramePrintAreaValid() ? '+' : '-');
+}
+
+static void lcl_Padded(OStringBuffer& rOut, const OString& s, size_t length)
+{
+ if (length < o3tl::make_unsigned(s.getLength()))
+ length = s.getLength();
+ rOut.append(s);
+ for (size_t i = 0; i < length - s.getLength(); i++)
+ {
+ rOut.append(" ");
+ }
+}
+
+static void lcl_Padded(OStringBuffer& rOut, const tools::Long n, size_t length = 5)
+{
+ char sz[RTL_STR_MAX_VALUEOFINT64];
+ rtl_str_valueOfInt64(sz, n, 10);
+ OString s(sz);
+ lcl_Padded(rOut, s, length);
+}
+
+/// output the frame as plain text.
+static void lcl_FrameRect(OStringBuffer& rOut, const char* hint, const SwRect& rect)
+{
+ rOut.append("[");
+ rOut.append(hint);
+ rOut.append(":X:");
+ lcl_Padded(rOut, rect.Pos().X());
+ rOut.append(", Y:");
+ lcl_Padded(rOut, rect.Pos().Y());
+ rOut.append(", Width:");
+ lcl_Padded(rOut, rect.SSize().Width());
+ rOut.append(", Height:");
+ lcl_Padded(rOut, rect.SSize().Height());
+ rOut.append("] ");
+}
+
+static OString lcl_TableInfo(const SwTabFrame* pTabFrame)
+{
+ const SwTable* pTable = pTabFrame->GetTable();
+ const SwFormat* pFormat = static_cast<const SwFormat*>(pTable->GetRegisteredIn());
+ const OUString& text = pFormat->GetName();
+ return OUStringToOString(text, RTL_TEXTENCODING_ASCII_US);
+}
+
+static OString lcl_RowInfo(const SwRowFrame* pFrame)
+{
+ // dummy, needs actual functionality...
+ if (pFrame == nullptr)
+ return "";
+ const SwTableLine* pTabLine = pFrame->GetTabLine();
+ if (pTabLine == nullptr)
+ return "";
+
+ return "RowInfo";
+}
+
+static OUString lcl_CellText(const SwCellFrame* pFrame)
+{
+ OUString result;
+ int n = 0;
+
+ const SwStartNode* pStartNode = pFrame->GetTabBox()->GetSttNd();
+ const SwEndNode* pEndNode = pStartNode->EndOfSectionNode();
+ const SwNodes& nodes = pStartNode->GetNodes();
+
+ for (SwNodeOffset i = pStartNode->GetIndex(); i < nodes.Count(); i++)
+ {
+ SwNode* pNode = nodes[i];
+
+ if (pNode->IsEndNode())
+ {
+ if (pNode->EndOfSectionNode() == pEndNode)
+ break;
+ }
+ else if (pNode->IsTextNode())
+ {
+ n++;
+ result += "Para:" + OUString::number(10) + " " +
+ pNode->GetTextNode()->GetText();
+ }
+ }
+
+ return OUString::number(n) + " para(s):" + result;
+}
+
+static OString lcl_CellInfo(const SwCellFrame* pFrame)
+{
+ const OUString text = "CellInfo: " + pFrame->GetTabBox()->GetName() + " Text: " + lcl_CellText(pFrame);
+ return OUStringToOString(text, RTL_TEXTENCODING_ASCII_US);
+}
+
+/// output the type of the frame as plain text.
+static void lcl_FrameType( OStringBuffer& rOut, const SwFrame* pFrame )
+{
+ if( pFrame->IsTextFrame() )
+ rOut.append("SwTextFrame ");
+ else if( pFrame->IsLayoutFrame() )
+ {
+ if( pFrame->IsPageFrame() )
+ rOut.append("SwPageFrame ");
+ else if( pFrame->IsColumnFrame() )
+ rOut.append("SwColumnFrame ");
+ else if( pFrame->IsBodyFrame() )
+ {
+ if( pFrame->GetUpper() && pFrame->IsColBodyFrame() )
+ rOut.append("(Col)");
+ rOut.append("SwBodyFrame ");
+ }
+ else if( pFrame->IsRootFrame() )
+ rOut.append("SwRootFrame ");
+ else if( pFrame->IsCellFrame() )
+ rOut.append("SwCellFrame ");
+ else if( pFrame->IsTabFrame() )
+ rOut.append("SwTabFrame ");
+ else if( pFrame->IsRowFrame() )
+ rOut.append("SwRowFrame ");
+ else if( pFrame->IsSctFrame() )
+ rOut.append("SwSectionFrame ");
+ else if( pFrame->IsHeaderFrame() )
+ rOut.append("SwHeaderFrame ");
+ else if( pFrame->IsFooterFrame() )
+ rOut.append("SwFooterFrame ");
+ else if( pFrame->IsFootnoteFrame() )
+ rOut.append("SwFootnoteFrame ");
+ else if( pFrame->IsFootnoteContFrame() )
+ rOut.append("SwFootnoteContFrame ");
+ else if( pFrame->IsFlyFrame() )
+ rOut.append("SwFlyFrame ");
+ else
+ rOut.append("SwLayoutFrame ");
+ }
+ else if( pFrame->IsNoTextFrame() )
+ rOut.append("SwNoTextFrame");
+ else
+ rOut.append("Not impl. ");
+}
+
+/**
+ * Is only called if the PROTOCOL macro finds out,
+ * that this function should be recorded ( @see{SwProtocol::nRecord} ).
+ *
+ * In this method we also check if FrameId and frame type should be logged.
+ */
+void SwImplProtocol::Record_( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam )
+{
+ sal_uInt16 nSpecial = 0;
+ if( nSpecial ) // the possible debugger manipulations
+ {
+ sal_uInt16 nId = sal_uInt16(lcl_GetFrameId( pFrame ));
+ switch ( nSpecial )
+ {
+ case 1: InsertFrame( nId ); break;
+ case 2: DeleteFrame( nId ); break;
+ case 3:
+ m_pFrameIds.reset();
+ break;
+ case 4:
+ m_pStream.reset();
+ break;
+ }
+ return;
+ }
+ if (!m_pStream && !NewStream())
+ return; // still no stream
+
+ if (m_pFrameIds && !m_pFrameIds->count(sal_uInt16(lcl_GetFrameId(pFrame))))
+ return; // doesn't belong to the wished FrameIds
+
+ if (!(pFrame->GetType() & m_nTypes))
+ return; // the type is unwanted
+
+ if (1 == m_nTestMode && nFunction != PROT::TestFormat)
+ return; // we may only log inside a test formatting
+ bool bTmp = false;
+ OStringBuffer aOut(m_aLayer);
+ aOut.append(static_cast<sal_Int64>(lcl_GetFrameId(pFrame)));
+ aOut.append(' ');
+ lcl_FrameType( aOut, pFrame ); // then the frame type
+ switch ( nFunction ) // and the function
+ {
+ case PROT::MakeAll: aOut.append("SwFrame::MakeAll");
+ lcl_Start(aOut, m_aLayer, nAct);
+ if (nAct == DbgAction::Start)
+ lcl_Flags(aOut, pFrame);
+ break;
+ case PROT::MoveFwd: bTmp = true;
+ [[fallthrough]];
+ case PROT::MoveBack:
+ if (nFunction == (bTmp ? PROT::Init : PROT::FileInit))
+ aOut.append("SwFlowFrame::MoveFwd");
+ else
+ aOut.append("SwFlowFrame::MoveBwd");
+ lcl_Start(aOut, m_aLayer, nAct);
+ if( pParam )
+ {
+ aOut.append(' ');
+ aOut.append(static_cast<sal_Int32>(*static_cast<sal_uInt16*>(pParam)));
+ }
+ break;
+ case PROT::GrowTest:
+ aOut.append("SwFrame::Grow (test)");
+ lcl_Start(aOut, m_aLayer, nAct);
+ break;
+ case PROT::ShrinkTest:
+ aOut.append("SwFrame::Shrink (test)");
+ lcl_Start(aOut, m_aLayer, nAct);
+ break;
+ case PROT::AdjustN :
+ case PROT::Shrink: bTmp = true;
+ [[fallthrough]];
+ case PROT::Grow:
+ if (!bTmp)
+ aOut.append("SwFrame::Grow");
+ else
+ {
+ if (nFunction == PROT::Shrink)
+ aOut.append("SwFrame::Shrink");
+ else
+ aOut.append("SwFrame::AdjustNeighbourhood");
+ }
+ lcl_Start(aOut, m_aLayer, nAct);
+ if( pParam )
+ {
+ aOut.append(' ');
+ aOut.append(static_cast<sal_Int64>(*static_cast<tools::Long*>(pParam)));
+ }
+ break;
+ case PROT::PrintArea: aOut.append("PROT::PrintArea");
+ lcl_Start(aOut, m_aLayer, nAct);
+ break;
+ case PROT::Size: aOut.append("PROT::Size");
+ lcl_Start(aOut, m_aLayer, nAct);
+ aOut.append(' ');
+ aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Height()));
+ break;
+ case PROT::Leaf: aOut.append("SwFrame::GetPrev/NextSctLeaf");
+ lcl_Start(aOut, m_aLayer, nAct);
+ aOut.append(' ');
+ if (pParam)
+ {
+ aOut.append(' ');
+ aOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame*>(pParam))));
+ }
+ break;
+ case PROT::FileInit: FileInit();
+ aOut.append("Initialize");
+ break;
+ case PROT::Section: SectFunc(aOut, nAct, pParam);
+ break;
+ case PROT::Cut: bTmp = true;
+ [[fallthrough]];
+ case PROT::Paste:
+ if (bTmp)
+ aOut.append("PROT::Cut from ");
+ else
+ aOut.append("PROT::Paste to ");
+ aOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame*>(pParam))));
+ break;
+ case PROT::TestFormat:
+ aOut.append("SwTextFrame::TestFormat");
+ lcl_Start(aOut, m_aLayer, nAct);
+ if( DbgAction::Start == nAct )
+ m_nTestMode |= 2;
+ else
+ m_nTestMode &= ~2;
+ break;
+ case PROT::FrmChanges:
+ {
+ SwRect& rFrame = *static_cast<SwRect*>(pParam);
+ if( pFrame->getFrameArea().Pos() != rFrame.Pos() )
+ {
+ aOut.append("PosChg: (");
+ aOut.append(static_cast<sal_Int64>(rFrame.Left()));
+ aOut.append(", ");
+ aOut.append(static_cast<sal_Int64>(rFrame.Top()));
+ aOut.append(") -> (");
+ aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Left()));
+ aOut.append(", ");
+ aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Top()));
+ aOut.append(") ");
+ }
+ if( pFrame->getFrameArea().Height() != rFrame.Height() )
+ {
+ aOut.append("Height: ");
+ aOut.append(static_cast<sal_Int64>(rFrame.Height()));
+ aOut.append(" -> ");
+ aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Height()));
+ aOut.append(" ");
+ }
+ if( pFrame->getFrameArea().Width() != rFrame.Width() )
+ {
+ aOut.append("Width: ");
+ aOut.append(static_cast<sal_Int64>(rFrame.Width()));
+ aOut.append(" -> ");
+ aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Width()));
+ aOut.append(' ');
+ }
+ break;
+ }
+ default: break;
+ }
+
+ aOut.append(" ");
+ while (aOut.getLength() < 40) aOut.append(" ");
+ lcl_FrameRect(aOut, "SwFrame", pFrame->getFrameArea());
+
+ aOut.append(" ");
+ while (aOut.getLength() < 90) aOut.append(" ");
+ lcl_FrameRect(aOut, "SwPrint", pFrame->getFramePrintArea());
+
+ if (pFrame->IsTextFrame())
+ {
+ aOut.append(" ");
+ while (aOut.getLength() < 140) aOut.append(" ");
+ const OUString& text = static_cast<const SwTextFrame*>(pFrame)->GetText();
+ OString o = OUStringToOString(text, RTL_TEXTENCODING_ASCII_US);
+ aOut.append(o);
+ }
+ else if (pFrame->IsTabFrame())
+ {
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pFrame);
+ aOut.append(lcl_TableInfo(pTabFrame));
+ }
+ else if (pFrame->IsRowFrame())
+ {
+ const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pFrame);
+ aOut.append(lcl_RowInfo(pRowFrame));
+
+ }
+ else if (pFrame->IsCellFrame())
+ {
+ const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(pFrame);
+ aOut.append(lcl_CellInfo(pCellFrame));
+ }
+
+ SAL_INFO("sw.layout.debug", aOut.getStr());
+ m_pStream->WriteOString(aOut.makeStringAndClear());
+ (*m_pStream) << endl; // output
+ m_pStream->Flush(); // to the disk, so we can read it immediately
+ if (++m_nLineCount >= m_nMaxLines) // max number of lines reached?
+ {
+ SAL_WARN("sw.layout.debug", "max number of lines reached");
+ SwProtocol::SetRecord( PROT::FileInit ); // => end f logging
+ }
+}
+
+/// Handle the output of the SectionFrames.
+void SwImplProtocol::SectFunc(OStringBuffer &rOut, DbgAction nAct, void const * pParam)
+{
+ bool bTmp = false;
+ switch( nAct )
+ {
+ case DbgAction::Merge: rOut.append("Merge Section ");
+ rOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame const *>(pParam))));
+ break;
+ case DbgAction::CreateMaster: bTmp = true;
+ [[fallthrough]];
+ case DbgAction::CreateFollow: rOut.append("Create Section ");
+ if (bTmp)
+ rOut.append("Master to ");
+ else
+ rOut.append("Follow from ");
+ rOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame const *>(pParam))));
+ break;
+ case DbgAction::DelMaster: bTmp = true;
+ [[fallthrough]];
+ case DbgAction::DelFollow: rOut.append("Delete Section ");
+ if (bTmp)
+ rOut.append("Master to ");
+ else
+ rOut.append("Follow from ");
+ rOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame const *>(pParam))));
+ break;
+ default: break;
+ }
+}
+
+/**
+ * if pFrameIds==NULL all Frames will be logged. But as soon as pFrameIds are
+ * set, only the added FrameIds are being logged.
+ *
+ * @param nId new FrameId for logging
+ * @return TRUE if newly added, FALSE if FrameId is already under control
+ */
+void SwImplProtocol::InsertFrame( sal_uInt16 nId )
+{
+ if (!m_pFrameIds)
+ m_pFrameIds.reset(new std::set<sal_uInt16>);
+ if (m_pFrameIds->count(nId))
+ return;
+ m_pFrameIds->insert(nId);
+}
+
+/// Removes a FrameId from the pFrameIds array, so that it won't be logged anymore.
+void SwImplProtocol::DeleteFrame( sal_uInt16 nId )
+{
+ if (!m_pFrameIds)
+ return;
+ m_pFrameIds->erase(nId);
+}
+
+/*
+ * The task here is to find the right SwImplEnterLeave object based on the
+ * function; everything else is then done in his Ctor/Dtor.
+ */
+SwEnterLeave::SwEnterLeave( const SwFrame* pFrame, PROT nFunc, DbgAction nAct, void* pPar )
+{
+ if( !SwProtocol::Record( nFunc ) )
+ return;
+ switch( nFunc )
+ {
+ case PROT::AdjustN :
+ case PROT::Grow:
+ case PROT::Shrink : pImpl.reset( new SwSizeEnterLeave( pFrame, nFunc, nAct, pPar ) ); break;
+ case PROT::MoveFwd:
+ case PROT::MoveBack : pImpl.reset( new SwUpperEnterLeave( pFrame, nFunc, nAct, pPar ) ); break;
+ case PROT::FrmChanges : pImpl.reset( new SwFrameChangesLeave( pFrame, nFunc, nAct, pPar ) ); break;
+ default: pImpl.reset( new SwImplEnterLeave( pFrame, nFunc, nAct, pPar ) ); break;
+ }
+ pImpl->Enter();
+}
+
+/* This is not inline because we don't want the SwImplEnterLeave definition inside
+ * dbg_lay.hxx.
+ */
+SwEnterLeave::~SwEnterLeave()
+{
+ if (pImpl)
+ pImpl->Leave();
+}
+
+void SwImplEnterLeave::Enter()
+{
+ SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::Start, m_pParam);
+}
+
+void SwImplEnterLeave::Leave() {
+ SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::End, m_pParam);
+}
+
+void SwSizeEnterLeave::Leave()
+{
+ m_nFrameHeight = m_pFrame->getFrameArea().Height() - m_nFrameHeight;
+ SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::End, &m_nFrameHeight);
+}
+
+void SwUpperEnterLeave::Enter()
+{
+ m_nFrameId = m_pFrame->GetUpper() ? sal_uInt16(lcl_GetFrameId(m_pFrame->GetUpper())) : 0;
+ SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::Start, &m_nFrameId);
+}
+
+void SwUpperEnterLeave::Leave()
+{
+ m_nFrameId = m_pFrame->GetUpper() ? sal_uInt16(lcl_GetFrameId(m_pFrame->GetUpper())) : 0;
+ SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::End, &m_nFrameId);
+}
+
+void SwFrameChangesLeave::Enter()
+{
+}
+
+void SwFrameChangesLeave::Leave()
+{
+ if (m_pFrame->getFrameArea() != m_aFrame)
+ SwProtocol::Record(m_pFrame, PROT::FrmChanges, DbgAction::NONE, &m_aFrame);
+}
+
+#endif // DBG_UTIL
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/dumpfilter.cxx b/sw/source/core/layout/dumpfilter.cxx
new file mode 100644
index 000000000..21d1ec354
--- /dev/null
+++ b/sw/source/core/layout/dumpfilter.cxx
@@ -0,0 +1,164 @@
+/* -*- 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/.
+ */
+
+#include <dumpfilter.hxx>
+
+#include <wrtsh.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <docsh.hxx>
+#include <rootfrm.hxx>
+#include <unotxdoc.hxx>
+
+#include <comphelper/servicehelper.hxx>
+#include <unotools/mediadescriptor.hxx>
+
+#include <libxml/xmlwriter.h>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+ int writeCallback( void* pContext, const char* sBuffer, int nLen )
+ {
+ int written = nLen;
+
+ // Actually write bytes to XOutputSream
+ try
+ {
+ uno::XInterface* pObj = static_cast<uno::XInterface*>(pContext);
+ uno::Reference< io::XOutputStream > xOut( pObj, uno::UNO_QUERY_THROW );
+
+ // Don't output the terminating \0 to the xml or the file will be invalid
+ uno::Sequence< sal_Int8 > seq( nLen );
+ strncpy( reinterpret_cast<char *>(seq.getArray()), sBuffer, nLen );
+ xOut->writeBytes( seq );
+ }
+ catch (const uno::Exception&)
+ {
+ written = -1;
+ }
+
+ return written;
+ }
+
+ int closeCallback( void* pContext )
+ {
+ int result = 0;
+ try
+ {
+ uno::XInterface* pObj = static_cast<uno::XInterface*>(pContext);
+ uno::Reference< io::XOutputStream > xOut( pObj, uno::UNO_QUERY_THROW );
+ xOut->closeOutput( );
+ }
+ catch (const uno::Exception&)
+ {
+ result = -1;
+ }
+ return result;
+ }
+}
+
+namespace sw
+{
+
+ LayoutDumpFilter::LayoutDumpFilter( )
+ {
+ }
+
+ LayoutDumpFilter::~LayoutDumpFilter( )
+ {
+ }
+
+ // XFilter
+ sal_Bool LayoutDumpFilter::filter( const uno::Sequence< beans::PropertyValue >& aDescriptor )
+ {
+ bool bRet = false;
+
+ utl::MediaDescriptor aMediaDesc = aDescriptor;
+
+ // Get the output stream
+ uno::Reference< io::XOutputStream > xOut = aMediaDesc.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_OUTPUTSTREAM,
+ uno::Reference< io::XOutputStream >() );
+
+ // Actually get the SwRootFrame to call dumpAsXml
+ auto pXDoc = comphelper::getFromUnoTunnel<SwXTextDocument>(m_xSrcDoc);
+ if ( pXDoc )
+ {
+ SwRootFrame* pLayout = pXDoc->GetDocShell()->GetWrtShell()->GetLayout();
+
+ // Get sure that the whole layout is processed: set a visible area
+ // even though there isn't any need of it
+ pXDoc->GetDocShell()->GetWrtShell()->StartAction();
+ tools::Rectangle aRect( 0, 0, 26000, 21000 );
+ pXDoc->GetDocShell()->SetVisArea( aRect );
+ pLayout->InvalidateAllContent( SwInvalidateFlags::Size );
+ pXDoc->GetDocShell()->GetWrtShell()->EndAction();
+
+ // Dump the layout XML into the XOutputStream
+ xmlOutputBufferPtr outBuffer = xmlOutputBufferCreateIO(
+ writeCallback, closeCallback, static_cast<void*>(xOut.get()), nullptr );
+
+ xmlTextWriterPtr writer = xmlNewTextWriter( outBuffer );
+ xmlTextWriterSetIndent(writer, 1);
+ (void)xmlTextWriterStartDocument( writer, nullptr, nullptr, nullptr );
+
+ // TODO This doesn't export the whole XML file, whereas dumpAsXML() does it nicely
+ pLayout->dumpAsXml( writer );
+
+ (void)xmlTextWriterEndDocument( writer );
+ xmlFreeTextWriter( writer );
+
+ bRet = true;
+ }
+
+ return bRet;
+ }
+
+ void LayoutDumpFilter::cancel( )
+ {
+ }
+
+ // XExporter
+ void LayoutDumpFilter::setSourceDocument( const uno::Reference< lang::XComponent >& xDoc )
+ {
+ m_xSrcDoc = xDoc;
+ }
+
+ // XInitialization
+ void LayoutDumpFilter::initialize( const uno::Sequence< uno::Any >& )
+ {
+ }
+
+ // XServiceInfo
+ OUString LayoutDumpFilter::getImplementationName( )
+ {
+ return "com.sun.star.comp.Writer.LayoutDump";
+ }
+
+ sal_Bool LayoutDumpFilter::supportsService( const OUString& rServiceName )
+ {
+ return cppu::supportsService(this, rServiceName);
+ }
+
+ uno::Sequence< OUString > LayoutDumpFilter::getSupportedServiceNames()
+ {
+ return { "com.sun.star.document.ExportFilter" };
+ }
+
+} // Namespace sw
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_LayoutDump_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new sw::LayoutDumpFilter());
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/findfrm.cxx b/sw/source/core/layout/findfrm.cxx
new file mode 100644
index 000000000..3d92f2a9c
--- /dev/null
+++ b/sw/source/core/layout/findfrm.cxx
@@ -0,0 +1,1905 @@
+/* -*- 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 <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <cellfrm.hxx>
+#include <rowfrm.hxx>
+#include <swtable.hxx>
+#include <notxtfrm.hxx>
+#include <tabfrm.hxx>
+#include <sectfrm.hxx>
+#include <frmatr.hxx>
+#include <flyfrm.hxx>
+#include <ftnfrm.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtclbl.hxx>
+#include <txtfrm.hxx>
+#include <bodyfrm.hxx>
+#include <calbck.hxx>
+#include <viewopt.hxx>
+#include <ndtxt.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <IDocumentSettingAccess.hxx>
+
+
+/// Searches the first ContentFrame in BodyText below the page.
+SwLayoutFrame *SwFootnoteBossFrame::FindBodyCont()
+{
+ SwFrame *pLay = Lower();
+ while ( pLay && !pLay->IsBodyFrame() )
+ pLay = pLay->GetNext();
+ return static_cast<SwLayoutFrame*>(pLay);
+}
+
+/// Searches the last ContentFrame in BodyText below the page.
+SwContentFrame *SwPageFrame::FindLastBodyContent()
+{
+ SwContentFrame *pRet = FindFirstBodyContent();
+ SwContentFrame *pNxt = pRet;
+ while ( pNxt && pNxt->IsInDocBody() && IsAnLower( pNxt ) )
+ { pRet = pNxt;
+ pNxt = pNxt->FindNextCnt();
+ }
+ return pRet;
+}
+
+/**
+ * Checks if the frame contains one or more ContentFrame's anywhere in his
+ * subsidiary structure; if so the first found ContentFrame is returned.
+ */
+const SwContentFrame *SwLayoutFrame::ContainsContent() const
+{
+ //Search downwards the layout leaf and if there is no content, jump to the
+ //next leaf until content is found or we leave "this".
+ //Sections: Content next to sections would not be found this way (empty
+ //sections directly next to ContentFrame) therefore we need to recursively
+ //search for them even if it's more complex.
+
+ const SwLayoutFrame *pLayLeaf = this;
+ do
+ {
+ while ( (!pLayLeaf->IsSctFrame() || pLayLeaf == this ) &&
+ pLayLeaf->Lower() && pLayLeaf->Lower()->IsLayoutFrame() )
+ pLayLeaf = static_cast<const SwLayoutFrame*>(pLayLeaf->Lower());
+
+ if( pLayLeaf->IsSctFrame() && pLayLeaf != this )
+ {
+ const SwContentFrame *pCnt = pLayLeaf->ContainsContent();
+ if( pCnt )
+ return pCnt;
+ if( pLayLeaf->GetNext() )
+ {
+ if( pLayLeaf->GetNext()->IsLayoutFrame() )
+ {
+ pLayLeaf = static_cast<const SwLayoutFrame*>(pLayLeaf->GetNext());
+ continue;
+ }
+ else
+ return static_cast<const SwContentFrame*>(pLayLeaf->GetNext());
+ }
+ }
+ else if ( pLayLeaf->Lower() )
+ return static_cast<const SwContentFrame*>(pLayLeaf->Lower());
+
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ if( !IsAnLower( pLayLeaf) )
+ return nullptr;
+ } while( pLayLeaf );
+ return nullptr;
+}
+
+/**
+ * Calls ContainsAny first to reach the innermost cell. From there we walk back
+ * up to the first SwCellFrame. Since we use SectionFrames, ContainsContent()->GetUpper()
+ * is not enough anymore.
+ */
+const SwCellFrame *SwLayoutFrame::FirstCell() const
+{
+ const SwFrame* pCnt = ContainsAny();
+ while( pCnt && !pCnt->IsCellFrame() )
+ pCnt = pCnt->GetUpper();
+ return static_cast<const SwCellFrame*>(pCnt);
+}
+
+/** return ContentFrames, sections, and tables.
+ *
+ * @param _bInvestigateFootnoteForSections controls investigation of content of footnotes for sections.
+ * @see ContainsContent
+ */
+const SwFrame *SwLayoutFrame::ContainsAny( const bool _bInvestigateFootnoteForSections ) const
+{
+ //Search downwards the layout leaf and if there is no content, jump to the
+ //next leaf until content is found, we leave "this" or until we found
+ //a SectionFrame or a TabFrame.
+
+ const SwLayoutFrame *pLayLeaf = this;
+ const bool bNoFootnote = IsSctFrame() && !_bInvestigateFootnoteForSections;
+ do
+ {
+ while ( ( (!pLayLeaf->IsSctFrame() && !pLayLeaf->IsTabFrame())
+ || pLayLeaf == this ) &&
+ pLayLeaf->Lower() && pLayLeaf->Lower()->IsLayoutFrame() )
+ pLayLeaf = static_cast<const SwLayoutFrame*>(pLayLeaf->Lower());
+
+ if( ( pLayLeaf->IsTabFrame() || pLayLeaf->IsSctFrame() )
+ && pLayLeaf != this )
+ {
+ // Now we also return "deleted" SectionFrames so they can be
+ // maintained on SaveContent and RestoreContent
+ return pLayLeaf;
+ }
+ else if ( pLayLeaf->Lower() )
+ return static_cast<const SwContentFrame*>(pLayLeaf->Lower());
+
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ if( bNoFootnote && pLayLeaf && pLayLeaf->IsInFootnote() )
+ {
+ do
+ {
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ } while( pLayLeaf && pLayLeaf->IsInFootnote() );
+ }
+ if( !IsAnLower( pLayLeaf) )
+ return nullptr;
+ } while( pLayLeaf );
+ return nullptr;
+}
+
+bool SwLayoutFrame::ContainsDeleteForbiddenLayFrame() const
+{
+ if (IsDeleteForbidden())
+ {
+ return true;
+ }
+ for (SwFrame const* pFrame = Lower(); pFrame; pFrame = pFrame->GetNext())
+ {
+ if (!pFrame->IsLayoutFrame())
+ {
+ continue;
+ }
+ SwLayoutFrame const*const pLay(static_cast<SwLayoutFrame const*>(pFrame));
+ if (pLay->ContainsDeleteForbiddenLayFrame())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+const SwFrame* SwFrame::GetLower() const
+{
+ return IsLayoutFrame() ? static_cast<const SwLayoutFrame*>(this)->Lower() : nullptr;
+}
+
+SwFrame* SwFrame::GetLower()
+{
+ return IsLayoutFrame() ? static_cast<SwLayoutFrame*>(this)->Lower() : nullptr;
+}
+
+SwContentFrame* SwFrame::FindPrevCnt( )
+{
+ if ( GetPrev() && GetPrev()->IsContentFrame() )
+ return static_cast<SwContentFrame*>(GetPrev());
+ else
+ return FindPrevCnt_();
+}
+
+const SwContentFrame* SwFrame::FindPrevCnt() const
+{
+ if ( GetPrev() && GetPrev()->IsContentFrame() )
+ return static_cast<const SwContentFrame*>(GetPrev());
+ else
+ return const_cast<SwFrame*>(this)->FindPrevCnt_();
+}
+
+SwContentFrame *SwFrame::FindNextCnt( const bool _bInSameFootnote )
+{
+ if ( mpNext && mpNext->IsContentFrame() )
+ return static_cast<SwContentFrame*>(mpNext);
+ else
+ return FindNextCnt_( _bInSameFootnote );
+}
+
+const SwContentFrame *SwFrame::FindNextCnt( const bool _bInSameFootnote ) const
+{
+ if ( mpNext && mpNext->IsContentFrame() )
+ return static_cast<SwContentFrame*>(mpNext);
+ else
+ return const_cast<SwFrame*>(this)->FindNextCnt_( _bInSameFootnote );
+}
+
+bool SwLayoutFrame::IsAnLower( const SwFrame *pAssumed ) const
+{
+ const SwFrame *pUp = pAssumed;
+ while ( pUp )
+ {
+ if ( pUp == this )
+ return true;
+ if ( pUp->IsFlyFrame() )
+ pUp = static_cast<const SwFlyFrame*>(pUp)->GetAnchorFrame();
+ else
+ pUp = pUp->GetUpper();
+ }
+ return false;
+}
+
+/** method to check relative position of layout frame to
+ a given layout frame.
+
+ OD 08.11.2002 - refactoring of pseudo-local method <lcl_Apres(..)> in
+ <txtftn.cxx> for #104840#.
+
+ @param _aCheckRefLayFrame
+ constant reference of an instance of class <SwLayoutFrame> which
+ is used as the reference for the relative position check.
+
+ @return true, if <this> is positioned before the layout frame <p>
+*/
+bool SwLayoutFrame::IsBefore( const SwLayoutFrame* _pCheckRefLayFrame ) const
+{
+ OSL_ENSURE( !IsRootFrame() , "<IsBefore> called at a <SwRootFrame>.");
+ OSL_ENSURE( !_pCheckRefLayFrame->IsRootFrame() , "<IsBefore> called with a <SwRootFrame>.");
+
+ bool bReturn;
+
+ // check, if on different pages
+ const SwPageFrame *pMyPage = FindPageFrame();
+ const SwPageFrame *pCheckRefPage = _pCheckRefLayFrame->FindPageFrame();
+ if( pMyPage != pCheckRefPage )
+ {
+ // being on different page as check reference
+ bReturn = pMyPage->GetPhyPageNum() < pCheckRefPage->GetPhyPageNum();
+ }
+ else
+ {
+ // being on same page as check reference
+ // --> search my supreme parent <pUp>, which doesn't contain check reference.
+ const SwLayoutFrame* pUp = this;
+ while ( pUp->GetUpper() &&
+ !pUp->GetUpper()->IsAnLower( _pCheckRefLayFrame )
+ )
+ pUp = pUp->GetUpper();
+ if( !pUp->GetUpper() )
+ {
+ // can occur, if <this> is a fly frm
+ bReturn = false;
+ }
+ else
+ {
+ // travel through the next's of <pUp> and check if one of these
+ // contain the check reference.
+ const SwLayoutFrame* pUpNext = static_cast<const SwLayoutFrame*>(pUp->GetNext());
+ while ( pUpNext &&
+ !pUpNext->IsAnLower( _pCheckRefLayFrame ) )
+ {
+ pUpNext = static_cast<const SwLayoutFrame*>(pUpNext->GetNext());
+ }
+ bReturn = pUpNext != nullptr;
+ }
+ }
+
+ return bReturn;
+}
+
+// Local helper functions for GetNextLayoutLeaf
+
+static const SwFrame* lcl_FindLayoutFrame( const SwFrame* pFrame, bool bNext )
+{
+ const SwFrame* pRet = nullptr;
+ if ( pFrame->IsFlyFrame() )
+ pRet = bNext ? static_cast<const SwFlyFrame*>(pFrame)->GetNextLink() : static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink();
+ else
+ pRet = bNext ? pFrame->GetNext() : pFrame->GetPrev();
+
+ return pRet;
+}
+
+static const SwFrame* lcl_GetLower( const SwFrame* pFrame, bool bFwd )
+{
+ if ( !pFrame->IsLayoutFrame() )
+ return nullptr;
+
+ return bFwd ?
+ static_cast<const SwLayoutFrame*>(pFrame)->Lower() :
+ static_cast<const SwLayoutFrame*>(pFrame)->GetLastLower();
+}
+
+/**
+ * Finds the next layout leaf. This is a layout frame, which does not
+ * have a lower which is a LayoutFrame. That means, pLower can be 0 or a
+ * content frame.
+ *
+ * However, pLower may be a TabFrame
+ */
+const SwLayoutFrame *SwFrame::ImplGetNextLayoutLeaf( bool bFwd ) const
+{
+ const SwFrame *pFrame = this;
+ const SwLayoutFrame *pLayoutFrame = nullptr;
+ const SwFrame *p = nullptr;
+ bool bGoingUp = !bFwd; // false for forward, true for backward
+ do {
+
+ bool bGoingFwdOrBwd = false;
+
+ bool bGoingDown = !bGoingUp;
+ if (bGoingDown)
+ {
+ p = lcl_GetLower( pFrame, bFwd );
+ bGoingDown = nullptr != p;
+ }
+ if ( !bGoingDown )
+ {
+ // I cannot go down, because either I'm currently going up or
+ // because the is no lower.
+ // I'll try to go forward:
+ p = lcl_FindLayoutFrame( pFrame, bFwd );
+ bGoingFwdOrBwd = nullptr != p;
+ if ( !bGoingFwdOrBwd )
+ {
+ // I cannot go forward, because there is no next frame.
+ // I'll try to go up:
+ p = pFrame->GetUpper();
+ bGoingUp = nullptr != p;
+ if ( !bGoingUp )
+ {
+ // I cannot go up, because there is no upper frame.
+ return nullptr;
+ }
+ }
+ }
+
+ // If I could not go down or forward, I'll have to go up
+ bGoingUp = !bGoingFwdOrBwd && !bGoingDown;
+
+ pFrame = p;
+ p = lcl_GetLower( pFrame, true );
+
+ } while( ( p && !p->IsFlowFrame() ) ||
+ pFrame == this ||
+ nullptr == ( pLayoutFrame = pFrame->IsLayoutFrame() ? static_cast<const SwLayoutFrame*>(pFrame) : nullptr ) ||
+ pLayoutFrame->IsAnLower( this ) );
+
+ return pLayoutFrame;
+}
+
+/**
+ * Walk back inside the tree: grab the subordinate Frame if one exists and the
+ * last step was not moving up a level (this would lead to an infinite up/down
+ * loop!). With this we ensure that during walking back we search through all
+ * sub trees. If we walked downwards we have to go to the end of the chain first
+ * because we go backwards from the last Frame inside another Frame. Walking
+ * forward works the same.
+ *
+ * @warning fixes here may also need to be applied to the @{lcl_NextFrame} method above
+ */
+const SwContentFrame* SwContentFrame::ImplGetNextContentFrame( bool bFwd ) const
+{
+ const SwFrame *pFrame = this;
+ const SwContentFrame *pContentFrame = nullptr;
+ bool bGoingUp = false;
+ do {
+ const SwFrame *p = nullptr;
+ bool bGoingFwdOrBwd = false;
+
+ bool bGoingDown = !bGoingUp;
+ if (bGoingDown)
+ {
+ p = lcl_GetLower( pFrame, true ) ;
+ bGoingDown = nullptr != p;
+ }
+ if ( !bGoingDown )
+ {
+ p = lcl_FindLayoutFrame( pFrame, bFwd );
+ bGoingFwdOrBwd = nullptr != p;
+ if ( !bGoingFwdOrBwd )
+ {
+ p = pFrame->GetUpper();
+ bGoingUp = nullptr != p;
+ if ( !bGoingUp )
+ {
+ return nullptr;
+ }
+ }
+ }
+
+ bGoingUp = !(bGoingFwdOrBwd || bGoingDown);
+ assert(p);
+ if (!bFwd && bGoingDown)
+ {
+ while ( p->GetNext() )
+ p = p->GetNext();
+ }
+
+ pFrame = p;
+ } while ( nullptr == (pContentFrame = (pFrame->IsContentFrame() ? static_cast<const SwContentFrame*>(pFrame) : nullptr) ));
+
+ return pContentFrame;
+}
+
+SwPageFrame* SwFrame::ImplFindPageFrame()
+{
+ SwFrame *pRet = this;
+ while ( pRet && !pRet->IsPageFrame() )
+ {
+ if ( pRet->GetUpper() )
+ pRet = pRet->GetUpper();
+ else if ( pRet->IsFlyFrame() )
+ {
+ // #i28701# - use new method <GetPageFrame()>
+ const auto pFly(static_cast<SwFlyFrame*>(pRet));
+ pRet = pFly->GetPageFrame();
+ if (pRet == nullptr)
+ pRet = pFly->AnchorFrame();
+ }
+ else
+ return nullptr;
+ }
+ return static_cast<SwPageFrame*>(pRet);
+}
+
+SwFootnoteBossFrame* SwFrame::FindFootnoteBossFrame( bool bFootnotes )
+{
+ SwFrame *pRet = this;
+ // Footnote bosses can't exist inside a table; also sections with columns
+ // don't contain footnote texts there
+ if( pRet->IsInTab() )
+ pRet = pRet->FindTabFrame();
+
+ // tdf139336: put the footnotes into the page frame (instead of a column frame)
+ // to avoid maximizing the section to the full page.... if:
+ // - it is in a section
+ // - collect footnotes at section end (FootnoteAtEnd) is not set
+ // - columns are evenly distributed (=balanced) is not set
+ // Note 1: at page end, the footnotes have no multi-column-capability,
+ // so this fix is used only where there is better chance to help
+ // Note 2: If balanced is not set, there is a higher chance that the 1. column will reach
+ // the end of the page... if that happens the section will be maximized anyway.
+ // Note 3: The user will be able to easily choose the old layout (with multi-column footnotes)
+ // with this setting.
+ // similar case can be reached with a page break + FootnoteAtEnd setting
+ SwSectionFrame* pSectframe = pRet->FindSctFrame();
+ bool bMoveToPageFrame = false;
+ // tdf146704: only if it is really a footnote, not an endnote.
+ // tdf54465: compatibility flag to make old odt files keep these full page sections.
+ if (bFootnotes && pSectframe
+ && pSectframe->GetFormat()->getIDocumentSettingAccess().get(
+ DocumentSettingId::FOOTNOTE_IN_COLUMN_TO_PAGEEND))
+ {
+ SwSection* pSect = pSectframe->GetSection();
+ if (pSect) {
+ bool bNoBalance = pSect->GetFormat()->GetBalancedColumns().GetValue();
+ bool bFAtEnd = pSectframe->IsFootnoteAtEnd();
+ bMoveToPageFrame = !bFAtEnd && !bNoBalance;
+ }
+ }
+ while (pRet
+ && ((!bMoveToPageFrame && !pRet->IsFootnoteBossFrame())
+ || (bMoveToPageFrame && !pRet->IsPageFrame())))
+ {
+ if ( pRet->GetUpper() )
+ pRet = pRet->GetUpper();
+ else if ( pRet->IsFlyFrame() )
+ {
+ // #i28701# - use new method <GetPageFrame()>
+ if ( static_cast<SwFlyFrame*>(pRet)->GetPageFrame() )
+ pRet = static_cast<SwFlyFrame*>(pRet)->GetPageFrame();
+ else
+ pRet = static_cast<SwFlyFrame*>(pRet)->AnchorFrame();
+ }
+ else
+ return nullptr;
+ }
+ if( bFootnotes && pRet && pRet->IsColumnFrame() &&
+ !pRet->GetNext() && !pRet->GetPrev() )
+ {
+ SwSectionFrame* pSct = pRet->FindSctFrame();
+ OSL_ENSURE( pSct, "FindFootnoteBossFrame: Single column outside section?" );
+ if( !pSct->IsFootnoteAtEnd() )
+ return pSct->FindFootnoteBossFrame( true );
+ }
+ return static_cast<SwFootnoteBossFrame*>(pRet);
+}
+
+SwTabFrame* SwFrame::ImplFindTabFrame()
+{
+ SwFrame *pRet = this;
+ while ( !pRet->IsTabFrame() )
+ {
+ pRet = pRet->GetUpper();
+ if ( !pRet )
+ return nullptr;
+ }
+ return static_cast<SwTabFrame*>(pRet);
+}
+
+SwSectionFrame* SwFrame::ImplFindSctFrame()
+{
+ SwFrame *pRet = this;
+ while ( !pRet->IsSctFrame() )
+ {
+ pRet = pRet->GetUpper();
+ if ( !pRet )
+ return nullptr;
+ }
+ return static_cast<SwSectionFrame*>(pRet);
+}
+
+const SwBodyFrame* SwFrame::ImplFindBodyFrame() const
+{
+ const SwFrame *pRet = this;
+ while ( !pRet->IsBodyFrame() )
+ {
+ pRet = pRet->GetUpper();
+ if ( !pRet )
+ return nullptr;
+ }
+ return static_cast<const SwBodyFrame*>(pRet);
+}
+
+SwFootnoteFrame *SwFrame::ImplFindFootnoteFrame()
+{
+ SwFrame *pRet = this;
+ while ( !pRet->IsFootnoteFrame() )
+ {
+ pRet = pRet->GetUpper();
+ if ( !pRet )
+ return nullptr;
+ }
+ return static_cast<SwFootnoteFrame*>(pRet);
+}
+
+SwFlyFrame *SwFrame::ImplFindFlyFrame()
+{
+ SwFrame *pRet = this;
+ do
+ {
+ if ( pRet->IsFlyFrame() )
+ return static_cast<SwFlyFrame*>(pRet);
+ else
+ pRet = pRet->GetUpper();
+ } while ( pRet );
+ return nullptr;
+}
+
+SwFrame *SwFrame::FindColFrame()
+{
+ SwFrame *pFrame = this;
+ do
+ { pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsColumnFrame() );
+ return pFrame;
+}
+
+SwRowFrame *SwFrame::FindRowFrame()
+{
+ SwFrame *pFrame = this;
+ do
+ { pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsRowFrame() );
+ return dynamic_cast< SwRowFrame* >( pFrame );
+}
+
+SwFrame* SwFrame::FindFooterOrHeader()
+{
+ SwFrame* pRet = this;
+ do
+ {
+ if (pRet->GetType() & FRM_HEADFOOT) //header and footer
+ return pRet;
+ else if ( pRet->GetUpper() )
+ pRet = pRet->GetUpper();
+ else if ( pRet->IsFlyFrame() )
+ pRet = static_cast<SwFlyFrame*>(pRet)->AnchorFrame();
+ else
+ return nullptr;
+ } while ( pRet );
+ return pRet;
+}
+
+const SwFootnoteFrame* SwFootnoteContFrame::FindFootNote() const
+{
+ const SwFootnoteFrame* pRet = static_cast<const SwFootnoteFrame*>(Lower());
+ if( pRet && !pRet->GetAttr()->GetFootnote().IsEndNote() )
+ return pRet;
+ return nullptr;
+}
+
+const SwPageFrame* SwRootFrame::GetPageAtPos( const Point& rPt, const Size* pSize, bool bExtend ) const
+{
+ const SwPageFrame* pRet = nullptr;
+
+ SwRect aRect;
+ if ( pSize )
+ {
+ aRect.Pos() = rPt;
+ aRect.SSize( *pSize );
+ }
+
+ const SwFrame* pPage = Lower();
+
+ if ( !bExtend )
+ {
+ if( !getFrameArea().Contains( rPt ) )
+ return nullptr;
+
+ // skip pages above point:
+ while( pPage && rPt.Y() > pPage->getFrameArea().Bottom() )
+ pPage = pPage->GetNext();
+ }
+
+ OSL_ENSURE( GetPageNum() <= maPageRects.size(), "number of pages differs from page rect array size" );
+ size_t nPageIdx = 0;
+
+ while ( pPage && !pRet )
+ {
+ const SwRect& rBoundRect = bExtend ? maPageRects[ nPageIdx++ ] : pPage->getFrameArea();
+
+ if ( (!pSize && rBoundRect.Contains(rPt)) ||
+ (pSize && rBoundRect.Overlaps(aRect)) )
+ {
+ pRet = static_cast<const SwPageFrame*>(pPage);
+ }
+
+ pPage = pPage->GetNext();
+ }
+
+ return pRet;
+}
+
+bool SwRootFrame::IsBetweenPages(const Point& rPt) const
+{
+ if (!getFrameArea().Contains(rPt))
+ return false;
+
+ // top visible page
+ const SwFrame* pPage = Lower();
+ if (pPage == nullptr)
+ return false;
+
+ // skip pages above point:
+ while (pPage && rPt.Y() > pPage->getFrameArea().Bottom())
+ pPage = pPage->GetNext();
+
+ if (pPage &&
+ rPt.X() >= pPage->getFrameArea().Left() &&
+ rPt.X() <= pPage->getFrameArea().Right())
+ {
+ // Trivial case when we're right in between.
+ if (!pPage->getFrameArea().Contains(rPt))
+ return true;
+
+ // In normal mode the gap is large enough and
+ // header/footer mouse interaction competes with
+ // handling hide-whitespace within them.
+ // In hide-whitespace, however, the gap is too small
+ // for convenience and there are no headers/footers.
+ const SwViewShell *pSh = GetCurrShell();
+ if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden())
+ {
+ constexpr SwTwips constMargin = o3tl::convert(tools::Long(2), o3tl::Length::mm, o3tl::Length::twip);
+
+ // If we are really close to the bottom or top of a page.
+ const auto toEdge = std::min(std::abs(pPage->getFrameArea().Top() - rPt.Y()),
+ std::abs(pPage->getFrameArea().Bottom() - rPt.Y()));
+ return toEdge <= constMargin;
+ }
+ }
+
+ return false;
+}
+
+const SvxFormatBreakItem& SwFrame::GetBreakItem() const
+{
+ return GetAttrSet()->GetBreak();
+}
+
+const SwFormatPageDesc& SwFrame::GetPageDescItem() const
+{
+ return GetAttrSet()->GetPageDesc();
+}
+
+const SvxFormatBreakItem& SwTextFrame::GetBreakItem() const
+{
+ return GetTextNodeFirst()->GetSwAttrSet().GetBreak();
+}
+
+const SwFormatPageDesc& SwTextFrame::GetPageDescItem() const
+{
+ return GetTextNodeFirst()->GetSwAttrSet().GetPageDesc();
+}
+
+const SwAttrSet* SwFrame::GetAttrSet() const
+{
+ if (IsTextFrame())
+ {
+ return &static_cast<const SwTextFrame*>(this)->GetTextNodeForParaProps()->GetSwAttrSet();
+ }
+ else if (IsNoTextFrame())
+ {
+ return &static_cast<const SwNoTextFrame*>(this)->GetNode()->GetSwAttrSet();
+ }
+ else
+ {
+ assert(IsLayoutFrame());
+ return &static_cast<const SwLayoutFrame*>(this)->GetFormat()->GetAttrSet();
+ }
+}
+
+drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwFrame::getSdrAllFillAttributesHelper() const
+{
+ if (IsTextFrame())
+ {
+ return static_cast<const SwTextFrame*>(this)->GetTextNodeForParaProps()->getSdrAllFillAttributesHelper();
+ }
+ else if (IsNoTextFrame())
+ {
+ return static_cast<const SwNoTextFrame*>(this)->GetNode()->getSdrAllFillAttributesHelper();
+ }
+ else
+ {
+ return static_cast< const SwLayoutFrame* >(this)->GetFormat()->getSdrAllFillAttributesHelper();
+ }
+}
+
+bool SwFrame::supportsFullDrawingLayerFillAttributeSet() const
+{
+ if (IsContentFrame())
+ {
+ return true;
+ }
+ else
+ {
+ return static_cast< const SwLayoutFrame* >(this)->GetFormat()->supportsFullDrawingLayerFillAttributeSet();
+ }
+}
+
+/*
+ * SwFrame::FindNext_(), FindPrev_(), InvalidateNextPos()
+ * FindNextCnt_() visits tables and sections and only returns SwContentFrames.
+ *
+ * Description Invalidates the position of the next frame.
+ * This is the direct successor or in case of ContentFrames the next
+ * ContentFrame which sits in the same flow as I do:
+ * - body,
+ * - footnote,
+ * - in headers/footers the notification only needs to be forwarded
+ * inside the section
+ * - same for Flys
+ * - Contents in tabs remain only inside their cell
+ * - in principle tables behave exactly like the Contents
+ * - sections also
+ */
+// This helper function is an equivalent to the ImplGetNextContentFrame() method,
+// besides ContentFrames this function also returns TabFrames and SectionFrames.
+static SwFrame* lcl_NextFrame( SwFrame* pFrame )
+{
+ SwFrame *pRet = nullptr;
+ bool bGoingUp = false;
+ do {
+ SwFrame *p = nullptr;
+
+ bool bGoingFwd = false;
+ bool bGoingDown = !bGoingUp && pFrame->IsLayoutFrame();
+ if (bGoingDown)
+ {
+ p = static_cast<SwLayoutFrame*>(pFrame)->Lower();
+ bGoingDown = nullptr != p;
+ }
+ if( !bGoingDown )
+ {
+ p = pFrame->IsFlyFrame() ? static_cast<SwFlyFrame*>(pFrame)->GetNextLink() : pFrame->GetNext();
+ bGoingFwd = nullptr != p;
+ if ( !bGoingFwd )
+ {
+ p = pFrame->GetUpper();
+ bGoingUp = nullptr != p;
+ if ( !bGoingUp )
+ {
+ return nullptr;
+ }
+ }
+ }
+ bGoingUp = !(bGoingFwd || bGoingDown);
+ pFrame = p;
+ } while ( nullptr == (pRet = ( ( pFrame->IsContentFrame() || ( !bGoingUp &&
+ ( pFrame->IsTabFrame() || pFrame->IsSctFrame() ) ) )? pFrame : nullptr ) ) );
+ return pRet;
+}
+
+SwFrame *SwFrame::FindNext_()
+{
+ bool bIgnoreTab = false;
+ SwFrame *pThis = this;
+
+ if ( IsTabFrame() )
+ {
+ //The last Content of the table gets picked up and his follower is
+ //returned. To be able to deactivate the special case for tables
+ //(see below) bIgnoreTab will be set.
+ if ( static_cast<SwTabFrame*>(this)->GetFollow() )
+ return static_cast<SwTabFrame*>(this)->GetFollow();
+
+ pThis = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
+ if ( !pThis )
+ pThis = this;
+ bIgnoreTab = true;
+ }
+ else if ( IsSctFrame() )
+ {
+ //The last Content of the section gets picked and his follower is returned.
+ if ( static_cast<SwSectionFrame*>(this)->GetFollow() )
+ return static_cast<SwSectionFrame*>(this)->GetFollow();
+
+ pThis = static_cast<SwSectionFrame*>(this)->FindLastContent();
+ if ( !pThis )
+ pThis = this;
+ }
+ else if ( IsContentFrame() )
+ {
+ if( static_cast<SwContentFrame*>(this)->GetFollow() )
+ return static_cast<SwContentFrame*>(this)->GetFollow();
+ }
+ else if ( IsRowFrame() )
+ {
+ SwFrame* pMyUpper = GetUpper();
+ if ( pMyUpper->IsTabFrame() && static_cast<SwTabFrame*>(pMyUpper)->GetFollow() )
+ return static_cast<SwTabFrame*>(pMyUpper)->GetFollow()->GetLower();
+ else return nullptr;
+ }
+ else
+ return nullptr;
+
+ SwFrame* pRet = nullptr;
+ const bool bFootnote = pThis->IsInFootnote();
+ if ( !bIgnoreTab && pThis->IsInTab() )
+ {
+ SwLayoutFrame *pUp = pThis->GetUpper();
+ while (pUp && !pUp->IsCellFrame())
+ pUp = pUp->GetUpper();
+ assert(pUp && "Content flag says it's in table but it's not in cell.");
+ SwFrame* pNxt = pUp ? static_cast<SwCellFrame*>(pUp)->GetFollowCell() : nullptr;
+ if ( pNxt )
+ pNxt = static_cast<SwCellFrame*>(pNxt)->ContainsContent();
+ if ( !pNxt )
+ {
+ pNxt = lcl_NextFrame( pThis );
+ if (pUp && pUp->IsAnLower(pNxt))
+ pRet = pNxt;
+ }
+ else
+ pRet = pNxt;
+ }
+ else
+ {
+ const bool bBody = pThis->IsInDocBody();
+ SwFrame *pNxtCnt = lcl_NextFrame( pThis );
+ if ( pNxtCnt )
+ {
+ if ( bBody || bFootnote )
+ {
+ while ( pNxtCnt )
+ {
+ // OD 02.04.2003 #108446# - check for endnote, only if found
+ // next content isn't contained in a section, that collect its
+ // endnotes at its end.
+ bool bEndn = IsInSct() && !IsSctFrame() &&
+ ( !pNxtCnt->IsInSct() ||
+ !pNxtCnt->FindSctFrame()->IsEndnAtEnd()
+ );
+ if ( ( bBody && pNxtCnt->IsInDocBody() ) ||
+ ( pNxtCnt->IsInFootnote() &&
+ ( bFootnote ||
+ ( bEndn && pNxtCnt->FindFootnoteFrame()->GetAttr()->GetFootnote().IsEndNote() )
+ )
+ )
+ )
+ {
+ pRet = pNxtCnt->IsInTab() ? pNxtCnt->FindTabFrame()
+ : pNxtCnt;
+ break;
+ }
+ pNxtCnt = lcl_NextFrame( pNxtCnt );
+ }
+ }
+ else if ( pThis->IsInFly() )
+ {
+ pRet = pNxtCnt->IsInTab() ? pNxtCnt->FindTabFrame()
+ : pNxtCnt;
+ }
+ else //footer-/or header section
+ {
+ const SwFrame *pUp = pThis->GetUpper();
+ const SwFrame *pCntUp = pNxtCnt->GetUpper();
+ while ( pUp && pUp->GetUpper() &&
+ !pUp->IsHeaderFrame() && !pUp->IsFooterFrame() )
+ pUp = pUp->GetUpper();
+ while ( pCntUp && pCntUp->GetUpper() &&
+ !pCntUp->IsHeaderFrame() && !pCntUp->IsFooterFrame() )
+ pCntUp = pCntUp->GetUpper();
+ if ( pCntUp == pUp )
+ {
+ pRet = pNxtCnt->IsInTab() ? pNxtCnt->FindTabFrame()
+ : pNxtCnt;
+ }
+ }
+ }
+ }
+ if( pRet && pRet->IsInSct() )
+ {
+ SwSectionFrame* pSct = pRet->FindSctFrame();
+ //Footnotes in frames with columns must not return the section which
+ //contains the footnote
+ if( !pSct->IsAnLower( this ) &&
+ (!bFootnote || pSct->IsInFootnote() ) )
+ return pSct;
+ }
+ return pRet;
+}
+
+// #i27138# - add parameter <_bInSameFootnote>
+SwContentFrame *SwFrame::FindNextCnt_( const bool _bInSameFootnote )
+{
+ SwFrame *pThis = this;
+
+ if ( IsTabFrame() )
+ {
+ if ( static_cast<SwTabFrame*>(this)->GetFollow() )
+ {
+ pThis = static_cast<SwTabFrame*>(this)->GetFollow()->ContainsContent();
+ if( pThis )
+ return static_cast<SwContentFrame*>(pThis);
+ }
+ pThis = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
+ if ( !pThis )
+ return nullptr;
+ }
+ else if ( IsSctFrame() )
+ {
+ if ( static_cast<SwSectionFrame*>(this)->GetFollow() )
+ {
+ pThis = static_cast<SwSectionFrame*>(this)->GetFollow()->ContainsContent();
+ if( pThis )
+ return static_cast<SwContentFrame*>(pThis);
+ }
+ pThis = static_cast<SwSectionFrame*>(this)->FindLastContent();
+ if ( !pThis )
+ return nullptr;
+ }
+ else if ( IsContentFrame() && static_cast<SwContentFrame*>(this)->GetFollow() )
+ return static_cast<SwContentFrame*>(this)->GetFollow();
+
+ if ( pThis->IsContentFrame() )
+ {
+ const bool bBody = pThis->IsInDocBody();
+ const bool bFootnote = pThis->IsInFootnote();
+ SwContentFrame *pNxtCnt = static_cast<SwContentFrame*>(pThis)->GetNextContentFrame();
+ if ( pNxtCnt )
+ {
+ // #i27138#
+ if ( bBody || ( bFootnote && !_bInSameFootnote ) )
+ {
+ // handling for environments 'footnotes' and 'document body frames':
+ while ( pNxtCnt )
+ {
+ if ( (bBody && pNxtCnt->IsInDocBody()) ||
+ (bFootnote && pNxtCnt->IsInFootnote()) )
+ return pNxtCnt;
+ pNxtCnt = pNxtCnt->GetNextContentFrame();
+ }
+ }
+ // #i27138#
+ else if ( bFootnote && _bInSameFootnote )
+ {
+ // handling for environments 'each footnote':
+ // Assure that found next content frame belongs to the same footnotes
+ const SwFootnoteFrame* pFootnoteFrameOfNext( pNxtCnt->FindFootnoteFrame() );
+ const SwFootnoteFrame* pFootnoteFrameOfCurr( pThis->FindFootnoteFrame() );
+ OSL_ENSURE( pFootnoteFrameOfCurr,
+ "<SwFrame::FindNextCnt_() - unknown layout situation: current frame has to have an upper footnote frame." );
+ if ( pFootnoteFrameOfNext == pFootnoteFrameOfCurr )
+ {
+ return pNxtCnt;
+ }
+ else if ( pFootnoteFrameOfCurr->GetFollow() )
+ {
+ // next content frame has to be the first content frame
+ // in the follow footnote, which contains a content frame.
+ SwFootnoteFrame* pFollowFootnoteFrameOfCurr(
+ const_cast<SwFootnoteFrame*>(pFootnoteFrameOfCurr) );
+ pNxtCnt = nullptr;
+ do {
+ pFollowFootnoteFrameOfCurr = pFollowFootnoteFrameOfCurr->GetFollow();
+ pNxtCnt = pFollowFootnoteFrameOfCurr->ContainsContent();
+ } while ( !pNxtCnt && pFollowFootnoteFrameOfCurr->GetFollow() );
+ return pNxtCnt;
+ }
+ else
+ {
+ // current content frame is the last content frame in the
+ // footnote - no next content frame exists.
+ return nullptr;
+ }
+ }
+ else if ( pThis->IsInFly() )
+ // handling for environments 'unlinked fly frame' and
+ // 'group of linked fly frames':
+ return pNxtCnt;
+ else
+ {
+ // handling for environments 'page header' and 'page footer':
+ const SwFrame *pUp = pThis->GetUpper();
+ const SwFrame *pCntUp = pNxtCnt->GetUpper();
+ while ( pUp && pUp->GetUpper() &&
+ !pUp->IsHeaderFrame() && !pUp->IsFooterFrame() )
+ pUp = pUp->GetUpper();
+ while ( pCntUp && pCntUp->GetUpper() &&
+ !pCntUp->IsHeaderFrame() && !pCntUp->IsFooterFrame() )
+ pCntUp = pCntUp->GetUpper();
+ if ( pCntUp == pUp )
+ return pNxtCnt;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/** method to determine previous content frame in the same environment
+ for a flow frame (content frame, table frame, section frame)
+
+ OD 2005-11-30 #i27138#
+*/
+SwContentFrame* SwFrame::FindPrevCnt_()
+{
+ if ( !IsFlowFrame() )
+ {
+ // nothing to do, if current frame isn't a flow frame.
+ return nullptr;
+ }
+
+ SwContentFrame* pPrevContentFrame( nullptr );
+
+ // Because method <SwContentFrame::GetPrevContentFrame()> is used to travel
+ // through the layout, a content frame, at which the travel starts, is needed.
+ SwContentFrame* pCurrContentFrame = dynamic_cast<SwContentFrame*>(this);
+
+ // perform shortcut, if current frame is a follow, and
+ // determine <pCurrContentFrame>, if current frame is a table or section frame
+ if ( pCurrContentFrame && pCurrContentFrame->IsFollow() )
+ {
+ // previous content frame is its master content frame
+ pPrevContentFrame = pCurrContentFrame->FindMaster();
+ }
+ else if ( IsTabFrame() )
+ {
+ SwTabFrame* pTabFrame( static_cast<SwTabFrame*>(this) );
+ if ( pTabFrame->IsFollow() )
+ {
+ // previous content frame is the last content of its master table frame
+ pPrevContentFrame = pTabFrame->FindMaster()->FindLastContent();
+ }
+ else
+ {
+ // start content frame for the search is the first content frame of
+ // the table frame.
+ pCurrContentFrame = pTabFrame->ContainsContent();
+ }
+ }
+ else if ( IsSctFrame() )
+ {
+ SwSectionFrame* pSectFrame( static_cast<SwSectionFrame*>(this) );
+ if ( pSectFrame->IsFollow() )
+ {
+ // previous content frame is the last content of its master section frame
+ pPrevContentFrame = pSectFrame->FindMaster()->FindLastContent();
+ }
+ else
+ {
+ // start content frame for the search is the first content frame of
+ // the section frame.
+ pCurrContentFrame = pSectFrame->ContainsContent();
+ }
+ }
+
+ // search for next content frame, depending on the environment, in which
+ // the current frame is in.
+ if ( !pPrevContentFrame && pCurrContentFrame )
+ {
+ pPrevContentFrame = pCurrContentFrame->GetPrevContentFrame();
+ if ( pPrevContentFrame )
+ {
+ if ( pCurrContentFrame->IsInFly() )
+ {
+ // handling for environments 'unlinked fly frame' and
+ // 'group of linked fly frames':
+ // Nothing to do, <pPrevContentFrame> is the one
+ }
+ else
+ {
+ const bool bInDocBody = pCurrContentFrame->IsInDocBody();
+ const bool bInFootnote = pCurrContentFrame->IsInFootnote();
+ if ( bInDocBody )
+ {
+ // handling for environments 'footnotes' and 'document body frames':
+ // Assure that found previous frame is also in one of these
+ // environments. Otherwise, travel further
+ while ( pPrevContentFrame )
+ {
+ if ( ( bInDocBody && pPrevContentFrame->IsInDocBody() ) ||
+ ( bInFootnote && pPrevContentFrame->IsInFootnote() ) )
+ {
+ break;
+ }
+ pPrevContentFrame = pPrevContentFrame->GetPrevContentFrame();
+ }
+ }
+ else if ( bInFootnote )
+ {
+ // handling for environments 'each footnote':
+ // Assure that found next content frame belongs to the same footnotes
+ const SwFootnoteFrame* pFootnoteFrameOfPrev( pPrevContentFrame->FindFootnoteFrame() );
+ const SwFootnoteFrame* pFootnoteFrameOfCurr( pCurrContentFrame->FindFootnoteFrame() );
+ if ( pFootnoteFrameOfPrev != pFootnoteFrameOfCurr )
+ {
+ if ( pFootnoteFrameOfCurr->GetMaster() )
+ {
+ SwFootnoteFrame* pMasterFootnoteFrameOfCurr(
+ const_cast<SwFootnoteFrame*>(pFootnoteFrameOfCurr) );
+ pPrevContentFrame = nullptr;
+ // correct wrong loop-condition
+ do {
+ pMasterFootnoteFrameOfCurr = pMasterFootnoteFrameOfCurr->GetMaster();
+ pPrevContentFrame = pMasterFootnoteFrameOfCurr->FindLastContent();
+ } while ( !pPrevContentFrame &&
+ pMasterFootnoteFrameOfCurr->GetMaster() );
+ }
+ else
+ {
+ // current content frame is the first content in the
+ // footnote - no previous content exists.
+ pPrevContentFrame = nullptr;
+ }
+ }
+ }
+ else
+ {
+ // handling for environments 'page header' and 'page footer':
+ // Assure that found previous frame is also in the same
+ // page header respectively page footer as <pCurrContentFrame>
+ // Note: At this point it's clear that <pCurrContentFrame> has
+ // to be inside a page header or page footer and that
+ // neither <pCurrContentFrame> nor <pPrevContentFrame> are
+ // inside a fly frame.
+ // Thus, method <FindFooterOrHeader()> can be used.
+ OSL_ENSURE( pCurrContentFrame->FindFooterOrHeader(),
+ "<SwFrame::FindPrevCnt_()> - unknown layout situation: current frame should be in page header or page footer" );
+ OSL_ENSURE( !pPrevContentFrame->IsInFly(),
+ "<SwFrame::FindPrevCnt_()> - unknown layout situation: found previous frame should *not* be inside a fly frame." );
+ if ( pPrevContentFrame->FindFooterOrHeader() !=
+ pCurrContentFrame->FindFooterOrHeader() )
+ {
+ pPrevContentFrame = nullptr;
+ }
+ }
+ }
+ }
+ }
+
+ return pPrevContentFrame;
+}
+
+SwFrame *SwFrame::FindPrev_()
+{
+ bool bIgnoreTab = false;
+ SwFrame *pThis = this;
+
+ if ( IsTabFrame() )
+ {
+ //The first Content of the table gets picked up and his predecessor is
+ //returned. To be able to deactivate the special case for tables
+ //(see below) bIgnoreTab will be set.
+ if ( static_cast<SwTabFrame*>(this)->IsFollow() )
+ return static_cast<SwTabFrame*>(this)->FindMaster();
+ else
+ pThis = static_cast<SwTabFrame*>(this)->ContainsContent();
+ bIgnoreTab = true;
+ }
+
+ if ( pThis && pThis->IsContentFrame() )
+ {
+ SwContentFrame *pPrvCnt = static_cast<SwContentFrame*>(pThis)->GetPrevContentFrame();
+ if( !pPrvCnt )
+ return nullptr;
+ if ( !bIgnoreTab && pThis->IsInTab() )
+ {
+ SwLayoutFrame *pUp = pThis->GetUpper();
+ while (pUp && !pUp->IsCellFrame())
+ pUp = pUp->GetUpper();
+ assert(pUp && "Content flag says it's in table but it's not in cell.");
+ if (pUp && pUp->IsAnLower(pPrvCnt))
+ return pPrvCnt;
+ }
+ else
+ {
+ SwFrame* pRet;
+ const bool bBody = pThis->IsInDocBody();
+ const bool bFootnote = !bBody && pThis->IsInFootnote();
+ if ( bBody || bFootnote )
+ {
+ while ( pPrvCnt )
+ {
+ if ( (bBody && pPrvCnt->IsInDocBody()) ||
+ (bFootnote && pPrvCnt->IsInFootnote()) )
+ {
+ pRet = pPrvCnt->IsInTab() ? pPrvCnt->FindTabFrame()
+ : static_cast<SwFrame*>(pPrvCnt);
+ return pRet;
+ }
+ pPrvCnt = pPrvCnt->GetPrevContentFrame();
+ }
+ }
+ else if ( pThis->IsInFly() )
+ {
+ pRet = pPrvCnt->IsInTab() ? pPrvCnt->FindTabFrame()
+ : static_cast<SwFrame*>(pPrvCnt);
+ return pRet;
+ }
+ else // footer or header or Fly
+ {
+ const SwFrame *pUp = pThis->GetUpper();
+ const SwFrame *pCntUp = pPrvCnt->GetUpper();
+ while ( pUp && pUp->GetUpper() &&
+ !pUp->IsHeaderFrame() && !pUp->IsFooterFrame() )
+ pUp = pUp->GetUpper();
+ while ( pCntUp && pCntUp->GetUpper() )
+ pCntUp = pCntUp->GetUpper();
+ if ( pCntUp == pUp )
+ {
+ pRet = pPrvCnt->IsInTab() ? pPrvCnt->FindTabFrame()
+ : static_cast<SwFrame*>(pPrvCnt);
+ return pRet;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+void SwFrame::ImplInvalidateNextPos( bool bNoFootnote )
+{
+ SwFrame *pFrame = FindNext_();
+ if ( nullptr == pFrame )
+ return;
+
+ if( pFrame->IsSctFrame() )
+ {
+ while( pFrame && pFrame->IsSctFrame() )
+ {
+ if( static_cast<SwSectionFrame*>(pFrame)->GetSection() )
+ {
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pTmp )
+ pTmp->InvalidatePos();
+ else if( !bNoFootnote )
+ static_cast<SwSectionFrame*>(pFrame)->InvalidateFootnotePos();
+ if( !IsInSct() || FindSctFrame()->GetFollow() != pFrame )
+ pFrame->InvalidatePos();
+ return;
+ }
+ pFrame = pFrame->FindNext();
+ }
+ if( pFrame )
+ {
+ if ( pFrame->IsSctFrame())
+ {
+ // We need to invalidate the section's content so it gets
+ // the chance to flow to a different page.
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pTmp )
+ pTmp->InvalidatePos();
+ if( !IsInSct() || FindSctFrame()->GetFollow() != pFrame )
+ pFrame->InvalidatePos();
+ }
+ else
+ pFrame->InvalidatePos();
+ }
+ }
+ else
+ pFrame->InvalidatePos();
+}
+
+/** method to invalidate printing area of next frame
+
+ OD 09.01.2004 #i11859#
+
+ FME 2004-04-19 #i27145# Moved function from SwTextFrame to SwFrame
+*/
+void SwFrame::InvalidateNextPrtArea()
+{
+ // determine next frame
+ SwFrame* pNextFrame = FindNext();
+ // skip empty section frames and hidden text frames
+ {
+ while ( pNextFrame &&
+ ( ( pNextFrame->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pNextFrame)->GetSection() ) ||
+ ( pNextFrame->IsTextFrame() &&
+ static_cast<SwTextFrame*>(pNextFrame)->IsHiddenNow() ) ) )
+ {
+ pNextFrame = pNextFrame->FindNext();
+ }
+ }
+
+ // Invalidate printing area of found next frame
+ if ( !pNextFrame )
+ return;
+
+ if ( pNextFrame->IsSctFrame() )
+ {
+ // Invalidate printing area of found section frame, if
+ // (1) this text frame isn't in a section OR
+ // (2) found section frame isn't a follow of the section frame this
+ // text frame is in.
+ if ( !IsInSct() || FindSctFrame()->GetFollow() != pNextFrame )
+ {
+ pNextFrame->InvalidatePrt();
+ }
+
+ // Invalidate printing area of first content in found section.
+ SwFrame* pFstContentOfSctFrame =
+ static_cast<SwSectionFrame*>(pNextFrame)->ContainsAny();
+ if ( pFstContentOfSctFrame )
+ {
+ pFstContentOfSctFrame->InvalidatePrt();
+ }
+ }
+ else
+ {
+ pNextFrame->InvalidatePrt();
+ }
+}
+
+/// @returns true if the frame _directly_ sits in a section
+/// but not if it sits in a table which itself sits in a section.
+static bool lcl_IsInSectionDirectly( const SwFrame *pUp )
+{
+ bool bSeenColumn = false;
+
+ while( pUp )
+ {
+ if( pUp->IsColumnFrame() )
+ bSeenColumn = true;
+ else if( pUp->IsSctFrame() )
+ {
+ auto pSection = static_cast<const SwSectionFrame*>(pUp);
+ const SwFrame* pHeaderFooter = pSection->FindFooterOrHeader();
+ // When the section frame is not in header/footer:
+ // Allow move of frame in case our only column is not growable.
+ // Also allow if there is a previous section frame (to move back).
+ bool bAllowOutsideHeaderFooter = !pSection->Growable() || pSection->GetPrecede();
+ return bSeenColumn || (!pHeaderFooter && bAllowOutsideHeaderFooter);
+ }
+ else if( pUp->IsTabFrame() )
+ return false;
+ pUp = pUp->GetUpper();
+ }
+ return false;
+}
+
+/** determine, if frame is moveable in given environment
+
+ OD 08.08.2003 #110978#
+ method replaced 'old' method <sal_Bool IsMoveable() const>.
+ Determines, if frame is moveable in given environment. if no environment
+ is given (parameter _pLayoutFrame == 0), the movability in the actual
+ environment (<GetUpper()) is checked.
+*/
+bool SwFrame::IsMoveable( const SwLayoutFrame* _pLayoutFrame ) const
+{
+ bool bRetVal = false;
+
+ if ( !_pLayoutFrame )
+ {
+ _pLayoutFrame = GetUpper();
+ }
+
+ if ( _pLayoutFrame && IsFlowFrame() )
+ {
+ if ( _pLayoutFrame->IsInSct() && lcl_IsInSectionDirectly( _pLayoutFrame ) )
+ {
+ bRetVal = true;
+ }
+ else if ( _pLayoutFrame->IsInFly() ||
+ _pLayoutFrame->IsInDocBody() ||
+ _pLayoutFrame->IsInFootnote() )
+ {
+ // If IsMovable() is called before a MoveFwd() the method
+ // may return false if there is no NextCellLeaf. If
+ // IsMovable() is called before a MoveBwd() the method may
+ // return false if there is no PrevCellLeaf.
+ if ( _pLayoutFrame->IsInTab() && !IsTabFrame() &&
+ ( !IsContentFrame() || (!const_cast<SwFrame*>(this)->GetNextCellLeaf()
+ && !const_cast<SwFrame*>(this)->GetPrevCellLeaf()) )
+ )
+ {
+ bRetVal = false;
+ }
+ else
+ {
+ if ( _pLayoutFrame->IsInFly() )
+ {
+ // if fly frame has a follow (next linked fly frame),
+ // frame is moveable.
+ if ( const_cast<SwLayoutFrame*>(_pLayoutFrame)->FindFlyFrame()->GetNextLink() )
+ {
+ bRetVal = true;
+ }
+ else
+ {
+ // if environment is columned, frame is moveable, if
+ // it isn't in last column.
+ // search for column frame
+ const SwFrame* pCol = _pLayoutFrame;
+ while ( pCol && !pCol->IsColumnFrame() )
+ {
+ pCol = pCol->GetUpper();
+ }
+ // frame is moveable, if found column frame isn't last one.
+ if ( pCol && pCol->GetNext() )
+ {
+ bRetVal = true;
+ }
+ }
+ }
+ else if (!(_pLayoutFrame->IsInFootnote() && (IsTabFrame() || IsInTab())))
+ {
+ bRetVal = true;
+ }
+ }
+ }
+ }
+
+ return bRetVal;
+}
+
+void SwFrame::SetInfFlags()
+{
+ if ( !IsFlyFrame() && !GetUpper() ) //not yet pasted, no information available
+ return;
+
+ mbInfInvalid = mbInfBody = mbInfTab = mbInfFly = mbInfFootnote = mbInfSct = false;
+
+ SwFrame *pFrame = this;
+ if( IsFootnoteContFrame() )
+ mbInfFootnote = true;
+ do
+ {
+ // mbInfBody is only set in the page body, but not in the column body
+ if ( pFrame->IsBodyFrame() && !mbInfFootnote && pFrame->GetUpper()
+ && pFrame->GetUpper()->IsPageFrame() )
+ mbInfBody = true;
+ else if ( pFrame->IsTabFrame() || pFrame->IsCellFrame() )
+ {
+ mbInfTab = true;
+ }
+ else if ( pFrame->IsFlyFrame() )
+ mbInfFly = true;
+ else if ( pFrame->IsSctFrame() )
+ mbInfSct = true;
+ else if ( pFrame->IsFootnoteFrame() )
+ mbInfFootnote = true;
+
+ pFrame = pFrame->GetUpper();
+
+ } while ( pFrame && !pFrame->IsPageFrame() ); //there is nothing above the page
+}
+
+/** Updates the vertical or the righttoleft-flags.
+ *
+ * If the property is derived, it's from the upper or (for fly frames) from
+ * the anchor. Otherwise we've to call a virtual method to check the property.
+ */
+void SwFrame::SetDirFlags( bool bVert )
+{
+ if( bVert )
+ {
+ // OD 2004-01-21 #114969# - if derived, valid vertical flag only if
+ // vertical flag of upper/anchor is valid.
+ if( mbDerivedVert )
+ {
+ const SwFrame* pAsk = IsFlyFrame() ?
+ static_cast<SwFlyFrame*>(this)->GetAnchorFrame() : GetUpper();
+
+ OSL_ENSURE( pAsk != this, "Autsch! Stack overflow is about to happen" );
+
+ if( pAsk )
+ {
+ mbVertical = pAsk->IsVertical();
+ mbVertLR = pAsk->IsVertLR();
+ mbVertLRBT = pAsk->IsVertLRBT();
+
+ if ( !pAsk->mbInvalidVert )
+ mbInvalidVert = false;
+
+ if ( IsCellFrame() )
+ {
+ SwCellFrame* pPrv = static_cast<SwCellFrame*>(this)->GetPreviousCell();
+ if ( pPrv && !mbVertical && pPrv->IsVertical() )
+ {
+ mbVertical = pPrv->IsVertical();
+ mbVertLR = pPrv->IsVertLR();
+ mbVertLRBT = pPrv->IsVertLRBT();
+ }
+ }
+ }
+ }
+ else
+ CheckDirection( bVert );
+ }
+ else
+ {
+ bool bInv = false;
+ if( !mbDerivedR2L ) // CheckDirection is able to set bDerivedR2L!
+ CheckDirection( bVert );
+ if( mbDerivedR2L )
+ {
+ const SwFrame* pAsk = IsFlyFrame() ?
+ static_cast<SwFlyFrame*>(this)->GetAnchorFrame() : GetUpper();
+
+ OSL_ENSURE( pAsk != this, "Oops! Stack overflow is about to happen" );
+
+ if( pAsk )
+ mbRightToLeft = pAsk->IsRightToLeft();
+ if( !pAsk || pAsk->mbInvalidR2L )
+ bInv = mbInvalidR2L;
+ }
+ mbInvalidR2L = bInv;
+ }
+}
+
+SwLayoutFrame* SwFrame::GetNextCellLeaf()
+{
+ SwFrame* pTmpFrame = this;
+ while (pTmpFrame && !pTmpFrame->IsCellFrame())
+ pTmpFrame = pTmpFrame->GetUpper();
+
+ SAL_WARN_IF(!pTmpFrame, "sw.core", "SwFrame::GetNextCellLeaf() without cell");
+ return pTmpFrame ? static_cast<SwCellFrame*>(pTmpFrame)->GetFollowCell() : nullptr;
+}
+
+SwLayoutFrame* SwFrame::GetPrevCellLeaf()
+{
+ SwFrame* pTmpFrame = this;
+ while (pTmpFrame && !pTmpFrame->IsCellFrame())
+ pTmpFrame = pTmpFrame->GetUpper();
+
+ SAL_WARN_IF(!pTmpFrame, "sw.core", "SwFrame::GetNextPreviousLeaf() without cell");
+ return pTmpFrame ? static_cast<SwCellFrame*>(pTmpFrame)->GetPreviousCell() : nullptr;
+}
+
+static SwCellFrame* lcl_FindCorrespondingCellFrame( const SwRowFrame& rOrigRow,
+ const SwCellFrame& rOrigCell,
+ const SwRowFrame& rCorrRow,
+ bool bInFollow )
+{
+ SwCellFrame* pRet = nullptr;
+ const SwCellFrame* pCell = static_cast<const SwCellFrame*>(rOrigRow.Lower());
+ SwCellFrame* pCorrCell = const_cast<SwCellFrame*>(static_cast<const SwCellFrame*>(rCorrRow.Lower()));
+
+ while ( pCell != &rOrigCell && !pCell->IsAnLower( &rOrigCell ) )
+ {
+ pCell = static_cast<const SwCellFrame*>(pCell->GetNext());
+ pCorrCell = static_cast<SwCellFrame*>(pCorrCell->GetNext());
+ }
+
+ assert(pCell && pCorrCell && "lcl_FindCorrespondingCellFrame does not work");
+
+ if ( pCell != &rOrigCell )
+ {
+ // rOrigCell must be a lower of pCell. We need to recurse into the rows:
+ assert(pCell->Lower() && pCell->Lower()->IsRowFrame() &&
+ "lcl_FindCorrespondingCellFrame does not work");
+
+ const SwRowFrame* pRow = static_cast<const SwRowFrame*>(pCell->Lower());
+ while ( !pRow->IsAnLower( &rOrigCell ) )
+ pRow = static_cast<const SwRowFrame*>(pRow->GetNext());
+
+ SwRowFrame* pCorrRow = nullptr;
+ if ( bInFollow )
+ pCorrRow = pRow->GetFollowRow();
+ else
+ {
+ SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pCorrCell->GetLastLower());
+
+ if ( pTmpRow && pTmpRow->GetFollowRow() == pRow )
+ pCorrRow = pTmpRow;
+ }
+
+ if ( pCorrRow )
+ pRet = lcl_FindCorrespondingCellFrame( *pRow, rOrigCell, *pCorrRow, bInFollow );
+ }
+ else
+ pRet = pCorrCell;
+
+ return pRet;
+}
+
+// VERSION OF GetFollowCell() that assumes that we always have a follow flow line:
+SwCellFrame* SwCellFrame::GetFollowCell() const
+{
+ SwCellFrame* pRet = nullptr;
+
+ // NEW TABLES
+ // Covered cells do not have follow cells!
+ const tools::Long nRowSpan = GetLayoutRowSpan();
+ if ( nRowSpan < 1 )
+ return nullptr;
+
+ // find most upper row frame
+ const SwFrame* pRow = GetUpper();
+
+ while (pRow && (!pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame()))
+ pRow = pRow->GetUpper();
+
+ if (!pRow)
+ return nullptr;
+
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pRow->GetUpper());
+ if (!pTabFrame || !pTabFrame->GetFollow() || !pTabFrame->HasFollowFlowLine())
+ return nullptr;
+
+ const SwCellFrame* pThisCell = this;
+
+ // Get last cell of the current table frame that belongs to the rowspan:
+ if ( nRowSpan > 1 )
+ {
+ // optimization: Will end of row span be in last row or exceed row?
+ tools::Long nMax = 0;
+ while ( pRow->GetNext() && ++nMax < nRowSpan )
+ pRow = pRow->GetNext();
+
+ if ( !pRow->GetNext() )
+ {
+ pThisCell = &pThisCell->FindStartEndOfRowSpanCell( false );
+ pRow = pThisCell->GetUpper();
+ }
+ }
+
+ const SwRowFrame* pFollowRow = nullptr;
+ if ( !pRow->GetNext() &&
+ nullptr != ( pFollowRow = pRow->IsInSplitTableRow() ) &&
+ ( !pFollowRow->IsRowSpanLine() || nRowSpan > 1 ) )
+ pRet = lcl_FindCorrespondingCellFrame( *static_cast<const SwRowFrame*>(pRow), *pThisCell, *pFollowRow, true );
+
+ return pRet;
+}
+
+// VERSION OF GetPreviousCell() THAT ASSUMES THAT WE ALWAYS HAVE A FFL
+SwCellFrame* SwCellFrame::GetPreviousCell() const
+{
+ SwCellFrame* pRet = nullptr;
+
+ // NEW TABLES
+ // Covered cells do not have previous cells!
+ if ( GetLayoutRowSpan() < 1 )
+ return nullptr;
+
+ // find most upper row frame
+ const SwFrame* pRow = GetUpper();
+ while( !pRow->IsRowFrame() || (pRow->GetUpper() && !pRow->GetUpper()->IsTabFrame()) )
+ pRow = pRow->GetUpper();
+
+ OSL_ENSURE( pRow->GetUpper() && pRow->GetUpper()->IsTabFrame(), "GetPreviousCell without Table" );
+
+ const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pRow->GetUpper());
+
+ if ( pTab && pTab->IsFollow() )
+ {
+ const SwFrame* pTmp = pTab->GetFirstNonHeadlineRow();
+ const bool bIsInFirstLine = ( pTmp == pRow );
+
+ if ( bIsInFirstLine )
+ {
+ SwTabFrame *pMaster = pTab->FindMaster();
+ if ( pMaster && pMaster->HasFollowFlowLine() )
+ {
+ SwRowFrame* pMasterRow = static_cast<SwRowFrame*>(pMaster->GetLastLower());
+ if ( pMasterRow )
+ pRet = lcl_FindCorrespondingCellFrame( *static_cast<const SwRowFrame*>(pRow), *this, *pMasterRow, false );
+ if ( pRet && pRet->GetTabBox()->getRowSpan() < 1 )
+ pRet = &const_cast<SwCellFrame&>(pRet->FindStartEndOfRowSpanCell( true ));
+ }
+ }
+ }
+
+ return pRet;
+}
+
+// --> NEW TABLES
+const SwCellFrame& SwCellFrame::FindStartEndOfRowSpanCell( bool bStart ) const
+{
+ const SwTabFrame* pTableFrame = dynamic_cast<const SwTabFrame*>(GetUpper()->GetUpper());
+
+ if ( !bStart && pTableFrame && pTableFrame->IsFollow() && pTableFrame->IsInHeadline( *this ) )
+ return *this;
+
+ OSL_ENSURE( pTableFrame &&
+ ( (bStart && GetTabBox()->getRowSpan() < 1) ||
+ (!bStart && GetLayoutRowSpan() > 1) ),
+ "SwCellFrame::FindStartRowSpanCell: No rowspan, no table, no cookies" );
+
+ if ( pTableFrame )
+ {
+ const SwTable* pTable = pTableFrame->GetTable();
+
+ sal_uInt16 nMax = USHRT_MAX;
+ const SwFrame* pCurrentRow = GetUpper();
+ const bool bDoNotEnterHeadline = bStart && pTableFrame->IsFollow() &&
+ !pTableFrame->IsInHeadline( *pCurrentRow );
+
+ // check how many rows we are allowed to go up or down until we reach the end of
+ // the current table frame:
+ nMax = 0;
+ while ( bStart ? pCurrentRow->GetPrev() : pCurrentRow->GetNext() )
+ {
+ if ( bStart )
+ {
+ // do not enter a repeated headline:
+ if ( bDoNotEnterHeadline && pTableFrame->IsFollow() &&
+ pTableFrame->IsInHeadline( *pCurrentRow->GetPrev() ) )
+ break;
+
+ pCurrentRow = pCurrentRow->GetPrev();
+ }
+ else
+ pCurrentRow = pCurrentRow->GetNext();
+
+ ++nMax;
+ }
+
+ // By passing the nMax value for Find*OfRowSpan (in case of bCurrentTableOnly
+ // is set) we assure that we find a rMasterBox that has a SwCellFrame in
+ // the current table frame:
+ const SwTableBox& rMasterBox = bStart ?
+ GetTabBox()->FindStartOfRowSpan( *pTable, nMax ) :
+ GetTabBox()->FindEndOfRowSpan( *pTable, nMax );
+
+ SwIterator<SwCellFrame,SwFormat> aIter( *rMasterBox.GetFrameFormat() );
+
+ for ( SwCellFrame* pMasterCell = aIter.First(); pMasterCell; pMasterCell = aIter.Next() )
+ {
+ if ( pMasterCell->GetTabBox() == &rMasterBox )
+ {
+ const SwTabFrame* pMasterTable = static_cast<const SwTabFrame*>(pMasterCell->GetUpper()->GetUpper());
+
+ if ( pMasterTable == pTableFrame )
+ {
+ return *pMasterCell;
+ }
+ }
+ }
+ }
+
+ SAL_WARN("sw.core", "SwCellFrame::FindStartRowSpanCell: No result");
+
+ return *this;
+}
+
+// <-- NEW TABLES
+
+const SwRowFrame* SwFrame::IsInSplitTableRow() const
+{
+ OSL_ENSURE( IsInTab(), "IsInSplitTableRow should only be called for frames in tables" );
+
+ const SwFrame* pRow = this;
+
+ // find most upper row frame
+ while( pRow && ( !pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame() ) )
+ pRow = pRow->GetUpper();
+
+ if ( !pRow ) return nullptr;
+
+ OSL_ENSURE( pRow->GetUpper()->IsTabFrame(), "Confusion in table layout" );
+
+ const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pRow->GetUpper());
+
+ // If most upper row frame is a headline row, the current frame
+ // can't be in a split table row. Thus, add corresponding condition.
+ if ( pRow->GetNext() ||
+ pTab->GetTable()->IsHeadline(
+ *(static_cast<const SwRowFrame*>(pRow)->GetTabLine()) ) ||
+ !pTab->HasFollowFlowLine() ||
+ !pTab->GetFollow() )
+ return nullptr;
+
+ // skip headline
+ const SwRowFrame* pFollowRow = pTab->GetFollow()->GetFirstNonHeadlineRow();
+
+ OSL_ENSURE( pFollowRow, "SwFrame::IsInSplitTableRow() does not work" );
+
+ return pFollowRow;
+}
+
+const SwRowFrame* SwFrame::IsInFollowFlowRow() const
+{
+ OSL_ENSURE( IsInTab(), "IsInSplitTableRow should only be called for frames in tables" );
+
+ // find most upper row frame
+ const SwFrame* pRow = this;
+ while( pRow && ( !pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame() ) )
+ pRow = pRow->GetUpper();
+
+ if ( !pRow ) return nullptr;
+
+ OSL_ENSURE( pRow->GetUpper()->IsTabFrame(), "Confusion in table layout" );
+
+ const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pRow->GetUpper());
+
+ const SwTabFrame* pMaster = pTab->IsFollow() ? pTab->FindMaster() : nullptr;
+
+ if ( !pMaster || !pMaster->HasFollowFlowLine() )
+ return nullptr;
+
+ const SwFrame* pTmp = pTab->GetFirstNonHeadlineRow();
+ const bool bIsInFirstLine = ( pTmp == pRow );
+
+ if ( !bIsInFirstLine )
+ return nullptr;
+
+ const SwRowFrame* pMasterRow = static_cast<const SwRowFrame*>(pMaster->GetLastLower());
+ return pMasterRow;
+}
+
+bool SwFrame::IsInBalancedSection() const
+{
+ bool bRet = false;
+
+ if ( IsInSct() )
+ {
+ const SwSectionFrame* pSectionFrame = FindSctFrame();
+ if ( pSectionFrame )
+ bRet = pSectionFrame->IsBalancedSection();
+ }
+ return bRet;
+}
+
+const SwFrame* SwLayoutFrame::GetLastLower() const
+{
+ const SwFrame* pRet = Lower();
+ if ( !pRet )
+ return nullptr;
+ while ( pRet->GetNext() )
+ pRet = pRet->GetNext();
+ return pRet;
+}
+
+SwTextFrame* SwFrame::DynCastTextFrame()
+{
+ return IsTextFrame() ? static_cast<SwTextFrame*>(this) : nullptr;
+}
+
+const SwTextFrame* SwFrame::DynCastTextFrame() const
+{
+ return IsTextFrame() ? static_cast<const SwTextFrame*>(this) : nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flowfrm.cxx b/sw/source/core/layout/flowfrm.cxx
new file mode 100644
index 000000000..78c3a34ca
--- /dev/null
+++ b/sw/source/core/layout/flowfrm.cxx
@@ -0,0 +1,2736 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svx/svdobj.hxx>
+
+#include <anchoredobject.hxx>
+#include <bodyfrm.hxx>
+#include <swtable.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <fmtanchr.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtpdsc.hxx>
+#include <editeng/ulspitem.hxx>
+#include <tgrditem.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <editeng/pgrditem.hxx>
+#include <paratr.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <pagedesc.hxx>
+#include <layact.hxx>
+#include <flyfrm.hxx>
+#include <sectfrm.hxx>
+#include <section.hxx>
+#include <dbg_lay.hxx>
+#include <lineinfo.hxx>
+#include <fmtclbl.hxx>
+#include <sortedobjs.hxx>
+#include <layouter.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <calbck.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+
+bool SwFlowFrame::s_bMoveBwdJump = false;
+
+SwFlowFrame::SwFlowFrame( SwFrame &rFrame ) :
+ m_rThis( rFrame ),
+ m_pFollow( nullptr ),
+ m_pPrecede( nullptr ),
+ m_bLockJoin( false ),
+ m_bUndersized( false ),
+ m_bFlyLock( false )
+{}
+
+SwFlowFrame::~SwFlowFrame()
+{
+ if (m_pFollow)
+ {
+ m_pFollow->m_pPrecede = nullptr;
+ }
+ if (m_pPrecede)
+ {
+ m_pPrecede->m_pFollow = nullptr;
+ }
+}
+
+void SwFlowFrame::SetFollow(SwFlowFrame *const pFollow)
+{
+ if (m_pFollow)
+ {
+ assert(this == m_pFollow->m_pPrecede);
+ m_pFollow->m_pPrecede = nullptr;
+ }
+ m_pFollow = pFollow;
+ if (m_pFollow != nullptr)
+ {
+ if (m_pFollow->m_pPrecede) // re-chaining pFollow?
+ {
+ assert(m_pFollow == m_pFollow->m_pPrecede->m_pFollow);
+ m_pFollow->m_pPrecede->m_pFollow = nullptr;
+ }
+ m_pFollow->m_pPrecede = this;
+ }
+}
+
+/// @return true if any follow has the JoinLocked flag
+bool SwFlowFrame::HasLockedFollow() const
+{
+ const SwFlowFrame* pFrame = GetFollow();
+ while( pFrame )
+ {
+ if( pFrame->IsJoinLocked() )
+ return true;
+ pFrame = pFrame->GetFollow();
+ }
+ return false;
+}
+
+bool SwFlowFrame::IsKeepFwdMoveAllowed( bool bIgnoreMyOwnKeepValue )
+{
+ // If all the predecessors up to the first of the chain have
+ // the 'keep' attribute set, and the first of the chain's
+ // IsFwdMoveAllowed returns false, then we're not allowed to move.
+ SwFrame *pFrame = &m_rThis;
+ if ( !pFrame->IsInFootnote() ) {
+ if ( bIgnoreMyOwnKeepValue && pFrame->GetIndPrev() )
+ pFrame = pFrame->GetIndPrev();
+ do
+ { if ( pFrame->GetAttrSet()->GetKeep().GetValue() )
+ pFrame = pFrame->GetIndPrev();
+ else
+ return true;
+ } while ( pFrame );
+ }
+ //See IsFwdMoveAllowed()
+ bool bRet = false;
+ if ( pFrame && pFrame->GetIndPrev() )
+ bRet = true;
+ return bRet;
+}
+
+void SwFlowFrame::CheckKeep()
+{
+ // Kick off the "last" predecessor with a 'keep' attribute, because
+ // it's possible for the whole troop to move back.
+ SwFrame *pPre = m_rThis.GetIndPrev();
+ assert(pPre);
+ if( pPre->IsSctFrame() )
+ {
+ SwFrame *pLast = static_cast<SwSectionFrame*>(pPre)->FindLastContent();
+ if( pLast && pLast->FindSctFrame() == pPre )
+ pPre = pLast;
+ else
+ return;
+ }
+ SwFrame* pTmp;
+ bool bKeep;
+ while ( (bKeep = pPre->GetAttrSet()->GetKeep().GetValue()) &&
+ nullptr != ( pTmp = pPre->GetIndPrev() ) )
+ {
+ if( pTmp->IsSctFrame() )
+ {
+ SwFrame *pLast = static_cast<SwSectionFrame*>(pTmp)->FindLastContent();
+ if( pLast && pLast->FindSctFrame() == pTmp )
+ pTmp = pLast;
+ else
+ break;
+ }
+ pPre = pTmp;
+ }
+ if ( bKeep )
+ pPre->InvalidatePos();
+}
+
+namespace
+{
+/**
+ * Determines if the next content frame after rThis will require the full area of the parent body
+ * frame.
+ */
+bool IsNextContentFullPage(const SwFrame& rThis)
+{
+ const SwFrame* pNext = rThis.FindNextCnt();
+ if (!pNext)
+ {
+ return false;
+ }
+
+ const SwSortedObjs* pNextDrawObjs = pNext->GetDrawObjs();
+ if (!pNextDrawObjs || !pNextDrawObjs->size())
+ {
+ return false;
+ }
+
+ for (const auto& pDrawObj : *pNextDrawObjs)
+ {
+ if (!pDrawObj)
+ {
+ continue;
+ }
+
+ SwTwips nDrawObjHeight = pDrawObj->GetObjRectWithSpaces().Height();
+ const SwPageFrame* pPageFrame = pDrawObj->GetPageFrame();
+ if (!pPageFrame)
+ {
+ continue;
+ }
+
+ SwTwips nBodyHeight = pPageFrame->GetLower()->getFrameArea().Height();
+ if (nDrawObjHeight < nBodyHeight)
+ {
+ continue;
+ }
+
+ const SwFormatSurround& rSurround = pDrawObj->GetFrameFormat().GetSurround();
+ if (rSurround.GetSurround() != text::WrapTextMode_NONE)
+ {
+ continue;
+ }
+
+ // At this point the height of the draw object will use all the vertical available space,
+ // and also no wrapping will be performed, so all horizontal space will be taken as well.
+ return true;
+ }
+
+ return false;
+}
+}
+
+bool SwFlowFrame::IsKeep(SvxFormatKeepItem const& rKeep,
+ SvxFormatBreakItem const& rBreak,
+ bool const bCheckIfLastRowShouldKeep) const
+{
+ // 1. The keep attribute is ignored inside footnotes
+ // 2. For compatibility reasons, the keep attribute is
+ // ignored for frames inside table cells
+ // 3. If bBreakCheck is set to true, this function only checks
+ // if there are any break after attributes set at rAttrs
+ // or break before attributes set for the next content (or next table)
+ // 4. Keep is ignored if the next frame will require its own page.
+ bool bKeep = bCheckIfLastRowShouldKeep ||
+ ( !m_rThis.IsInFootnote() &&
+ ( !m_rThis.IsInTab() || m_rThis.IsTabFrame() ) &&
+ rKeep.GetValue() && !IsNextContentFullPage(m_rThis));
+
+ OSL_ENSURE( !bCheckIfLastRowShouldKeep || m_rThis.IsTabFrame(),
+ "IsKeep with bCheckIfLastRowShouldKeep should only be used for tabfrms" );
+
+ // Ignore keep attribute if there are break situations:
+ if ( bKeep )
+ {
+ switch (rBreak.GetBreak())
+ {
+ case SvxBreak::ColumnAfter:
+ case SvxBreak::ColumnBoth:
+ case SvxBreak::PageAfter:
+ case SvxBreak::PageBoth:
+ {
+ bKeep = false;
+ break;
+ }
+ default: break;
+ }
+ if ( bKeep )
+ {
+ SwFrame *pNxt;
+ if( nullptr != (pNxt = m_rThis.FindNextCnt()) &&
+ (!m_pFollow || pNxt != &m_pFollow->GetFrame()))
+ {
+ // The last row of a table only keeps with the next content
+ // it they are in the same section:
+ if ( bCheckIfLastRowShouldKeep )
+ {
+ const SwSection* pThisSection = nullptr;
+ const SwSection* pNextSection = nullptr;
+ const SwSectionFrame* pThisSectionFrame = m_rThis.FindSctFrame();
+ const SwSectionFrame* pNextSectionFrame = pNxt->FindSctFrame();
+
+ if ( pThisSectionFrame )
+ pThisSection = pThisSectionFrame->GetSection();
+
+ if ( pNextSectionFrame )
+ pNextSection = pNextSectionFrame->GetSection();
+
+ if ( pThisSection != pNextSection )
+ bKeep = false;
+ }
+
+ if ( bKeep )
+ {
+ SvxFormatBreakItem const* pBreak;
+ SwFormatPageDesc const* pPageDesc;
+ SwTabFrame* pTab = pNxt->IsInTab() ? pNxt->FindTabFrame() : nullptr;
+ if (pTab && (!m_rThis.IsInTab() || m_rThis.FindTabFrame() != pTab))
+ {
+ const SwAttrSet *const pSet = &pTab->GetFormat()->GetAttrSet();
+ pBreak = &pSet->GetBreak();
+ pPageDesc = &pSet->GetPageDesc();
+ }
+ else
+ {
+ pBreak = &pNxt->GetBreakItem();
+ pPageDesc = &pNxt->GetPageDescItem();
+ }
+
+ if (pPageDesc->GetPageDesc())
+ bKeep = false;
+ else switch (pBreak->GetBreak())
+ {
+ case SvxBreak::ColumnBefore:
+ case SvxBreak::ColumnBoth:
+ case SvxBreak::PageBefore:
+ case SvxBreak::PageBoth:
+ bKeep = false;
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+ }
+ return bKeep;
+}
+
+sal_uInt8 SwFlowFrame::BwdMoveNecessary( const SwPageFrame *pPage, const SwRect &rRect )
+{
+ // The return value helps deciding whether we need to flow back (3),
+ // or whether we can use the good old WouldFit (0, 1), or if
+ // it's reasonable to relocate and test-format (2).
+
+ // Bit 1 in this case means that there are objects anchored to myself,
+ // bit 2 means that I have to evade other objects.
+
+ // If a SurroundObj that desires to be wrapped around overlaps with the
+ // Rect, it's required to flow (because we can't guess the relationships).
+ // However it's possible for a test formatting to happen.
+ // If the SurroundObj is a Fly and I'm a Lower, or the Fly is a Lower of
+ // mine, then it doesn't matter.
+ // If the SurroundObj is anchored in a character bound Fly, and I'm not
+ // a Lower of that character bound Fly myself, then the Fly doesn't matter.
+
+ // If the object is anchored with me, i can ignore it, because
+ // it's likely that it will follow me with the flow. A test formatting is
+ // not allowed in that case, however!
+ sal_uInt8 nRet = 0;
+ SwFlowFrame *pTmp = this;
+ do
+ { // If there are objects hanging either on me or on a follow, we can't
+ // do a test formatting, because paragraph bound objects wouldn't
+ // be properly considered, and character bound objects shouldn't
+ // be test formatted at all.
+ if( pTmp->GetFrame().GetDrawObjs() )
+ nRet = 1;
+ pTmp = pTmp->GetFollow();
+ } while ( !nRet && pTmp );
+ const SwSortedObjs *pObjs = pPage ? pPage->GetSortedObjs() : nullptr;
+ if (pObjs)
+ {
+
+ const SwSortedObjs &rObjs = *pObjs;
+ SwNodeOffset nIndex = NODE_OFFSET_MAX;
+ for ( size_t i = 0; nRet < 3 && i < rObjs.size(); ++i )
+ {
+
+ SwAnchoredObject* pObj = rObjs[i];
+ const SwFrameFormat& rFormat = pObj->GetFrameFormat();
+ const SwRect aRect( pObj->GetObjRect() );
+ if ( aRect.Overlaps( rRect ) &&
+ rFormat.GetSurround().GetSurround() != css::text::WrapTextMode_THROUGH )
+ {
+ if( m_rThis.IsLayoutFrame() && //Fly Lower of This?
+ Is_Lower_Of( &m_rThis, pObj->GetDrawObj() ) )
+ continue;
+ if( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsAnLower( &m_rThis ) )//This Lower of Fly?
+ continue;
+ }
+
+ const SwFrame* pAnchor = pObj->GetAnchorFrame();
+ if ( pAnchor == &m_rThis )
+ {
+ nRet |= 1;
+ continue;
+ }
+
+ // Don't do this if the object is anchored behind me in the text
+ // flow, because then I wouldn't evade it.
+ if ( ::IsFrameInSameContext( pAnchor, &m_rThis ) )
+ {
+ if ( rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PARA )
+ {
+ // The index of the other one can be retrieved using the anchor attribute.
+ SwNodeOffset nTmpIndex = rFormat.GetAnchor().GetContentAnchor()->nNode.GetIndex();
+ // Now we're going to check whether the current paragraph before
+ // the anchor of the displacing object sits in the text. If this
+ // is the case, we don't try to evade it.
+ // The index is being determined via SwFormatAnchor, because it's
+ // getting quite expensive otherwise.
+ if( NODE_OFFSET_MAX == nIndex )
+ {
+ const SwNode *pNode;
+ if (m_rThis.IsTextFrame())
+ pNode = static_cast<SwTextFrame&>(m_rThis).GetTextNodeFirst();
+ else if (m_rThis.IsNoTextFrame())
+ pNode = static_cast<SwNoTextFrame&>(m_rThis).GetNode();
+ else if( m_rThis.IsSctFrame() )
+ pNode = static_cast<SwSectionFormat*>(static_cast<SwSectionFrame&>(m_rThis).
+ GetFormat())->GetSectionNode();
+ else
+ {
+ assert(!m_rThis.IsContentFrame());
+ OSL_ENSURE( m_rThis.IsTabFrame(), "new FowFrame?" );
+ pNode = static_cast<SwTabFrame&>(m_rThis).GetTable()->
+ GetTabSortBoxes()[0]->GetSttNd()->FindTableNode();
+ }
+ nIndex = pNode->GetIndex();
+ }
+ if (nIndex < nTmpIndex &&
+ (!m_rThis.IsTextFrame() ||
+ !FrameContainsNode(static_cast<SwTextFrame&>(m_rThis), nTmpIndex)))
+ {
+ continue;
+ }
+ }
+ }
+ else
+ continue;
+
+ nRet |= 2;
+ }
+ }
+ }
+ return nRet;
+}
+
+/// A specialized form of Cut(), which relocates a whole chain (this and the following,
+/// in particular). During this process, only the minimum operations and notifications are done.
+SwLayoutFrame *SwFlowFrame::CutTree( SwFrame *pStart )
+{
+ // Cut the Start and all the neighbours; they are chained together and
+ // a handle to the first one is returned. Residuals are invalidated
+ // as appropriate.
+
+ SwLayoutFrame *pLay = pStart->GetUpper();
+ if ( pLay->IsInFootnote() )
+ pLay = pLay->FindFootnoteFrame();
+
+ // i#58846
+ // <pPrepare( PrepareHint::QuoVadis )> only for frames in footnotes
+ if( pStart->IsInFootnote() )
+ {
+ SwFrame* pTmp = pStart->GetIndPrev();
+ if( pTmp )
+ pTmp->Prepare( PrepareHint::QuoVadis );
+ }
+
+ // Just cut quickly and take care that we don't cause problems with the
+ // left-behinds. The pointers of the chain being cut can point who-knows where.
+ if ( pStart == pStart->GetUpper()->Lower() )
+ pStart->GetUpper()->m_pLower = nullptr;
+ if ( pStart->GetPrev() )
+ {
+ pStart->GetPrev()->mpNext = nullptr;
+ pStart->mpPrev = nullptr;
+ }
+
+ if ( pLay->IsFootnoteFrame() )
+ {
+ if ( !pLay->Lower() && !pLay->IsColLocked() &&
+ !static_cast<SwFootnoteFrame*>(pLay)->IsBackMoveLocked() )
+ {
+ // tdf#101821 don't delete it while iterating over it
+ if (!pLay->IsDeleteForbidden())
+ {
+ pLay->Cut();
+ SwFrame::DestroyFrame(pLay);
+ }
+ // else: assume there is code on the stack to clean up empty
+ // footnote frames
+ // (don't go into the else branch below, it produces a disconnected
+ // footnote with null upper that can be returned by
+ // SwFootnoteBossFrame::FindFootnote() causing null pointer deref
+ // in SwTextFrame::ConnectFootnote()
+ }
+ else
+ {
+ bool bUnlock = !static_cast<SwFootnoteFrame*>(pLay)->IsBackMoveLocked();
+ static_cast<SwFootnoteFrame*>(pLay)->LockBackMove();
+ pLay->InvalidateSize();
+ pLay->Calc(pLay->getRootFrame()->GetCurrShell()->GetOut());
+ SwContentFrame *pCnt = pLay->ContainsContent();
+ while ( pCnt && pLay->IsAnLower( pCnt ) )
+ {
+ // It's possible for the ContentFrame to be locked, and we don't want
+ // to end up in an endless page migration, so we're not even
+ // going to call Calc!
+ OSL_ENSURE( pCnt->IsTextFrame(), "The Graphic has landed." );
+ if ( static_cast<SwTextFrame*>(pCnt)->IsLocked() ||
+ static_cast<SwTextFrame*>(pCnt)->GetFollow() == pStart )
+ break;
+ pCnt->Calc(pCnt->getRootFrame()->GetCurrShell()->GetOut());
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ if( bUnlock )
+ static_cast<SwFootnoteFrame*>(pLay)->UnlockBackMove();
+ }
+ pLay = nullptr;
+ }
+ return pLay;
+}
+
+/// A specialized form of Paste(), which relocates a whole chain (this and the following,
+/// in particular). During this process, only the minimum operations and notifications are done.
+bool SwFlowFrame::PasteTree( SwFrame *pStart, SwLayoutFrame *pParent, SwFrame *pSibling,
+ SwFrame *pOldParent )
+{
+ // returns true if there's a LayoutFrame in the chain.
+ bool bRet = false;
+
+ // The chain beginning with pStart is inserted before pSibling
+ // under the parent. We take care to invalidate as required.
+
+ // I'm receiving a finished chain. We need to update the pointers for
+ // the beginning of the chain, then all the uppers and finally the end.
+ // On the way there, we invalidate as required.
+ if ( pSibling )
+ {
+ pStart->mpPrev = pSibling->GetPrev();
+ if ( nullptr != pStart->mpPrev )
+ pStart->GetPrev()->mpNext = pStart;
+ else
+ pParent->m_pLower = pStart;
+ pSibling->InvalidatePos_();
+ pSibling->InvalidatePrt_();
+ }
+ else
+ {
+ pStart->mpPrev = pParent->Lower();
+ if ( nullptr == pStart->mpPrev )
+ pParent->m_pLower = pStart;
+ else
+ //i#100782
+ //If the pParent has more than 1 child nodes, former design will
+ //ignore them directly without any collection work. It will make some
+ //dangling pointers. This lead the crash...
+ //The new design will find the last child of pParent in loop way, and
+ //add the pStart after the last child.
+ // pParent->Lower()->pNext = pStart;
+ {
+ SwFrame* pTemp = pParent->m_pLower;
+ while (pTemp)
+ {
+ if (pTemp->mpNext)
+ pTemp = pTemp->mpNext;
+ else
+ {
+ pStart->mpPrev = pTemp;
+ pTemp->mpNext = pStart;
+ break;
+ }
+ }
+ }
+
+
+ // i#27145
+ if ( pParent->IsSctFrame() )
+ {
+ // We have no sibling because pParent is a section frame and
+ // has just been created to contain some content. The printing
+ // area of the frame behind pParent has to be invalidated, so
+ // that the correct distance between pParent and the next frame
+ // can be calculated.
+ pParent->InvalidateNextPrtArea();
+ }
+ }
+ SwFrame *pFloat = pStart;
+ SwFrame *pLst = nullptr;
+ SwRectFnSet aRectFnSet(pParent);
+ SwTwips nGrowVal = 0;
+ do
+ { pFloat->mpUpper = pParent;
+ pFloat->InvalidateAll_();
+ pFloat->CheckDirChange();
+
+ // I'm a friend of the TextFrame and thus am allowed to do many things.
+ // The CacheIdx idea seems to be a bit risky!
+ if ( pFloat->IsTextFrame() )
+ {
+ if ( static_cast<SwTextFrame*>(pFloat)->GetCacheIdx() != USHRT_MAX )
+ static_cast<SwTextFrame*>(pFloat)->Init(); // I'm his friend.
+ }
+ else
+ bRet = true;
+
+ nGrowVal += aRectFnSet.GetHeight(pFloat->getFrameArea());
+ if ( pFloat->GetNext() )
+ pFloat = pFloat->GetNext();
+ else
+ {
+ pLst = pFloat;
+ pFloat = nullptr;
+ }
+ } while ( pFloat );
+
+ if ( pSibling )
+ {
+ pLst->mpNext = pSibling;
+ pSibling->mpPrev = pLst;
+ if( pSibling->IsInFootnote() )
+ {
+ if( pSibling->IsSctFrame() )
+ pSibling = static_cast<SwSectionFrame*>(pSibling)->ContainsAny();
+ if( pSibling )
+ pSibling->Prepare( PrepareHint::ErgoSum );
+ }
+ }
+ if ( nGrowVal )
+ {
+ if ( pOldParent && pOldParent->IsBodyFrame() ) // For variable page height while browsing
+ pOldParent->Shrink( nGrowVal );
+ pParent->Grow( nGrowVal );
+ }
+
+ if ( pParent->IsFootnoteFrame() )
+ static_cast<SwFootnoteFrame*>(pParent)->InvalidateNxtFootnoteCnts( pParent->FindPageFrame() );
+ return bRet;
+}
+
+void SwFlowFrame::MoveSubTree( SwLayoutFrame* pParent, SwFrame* pSibling )
+{
+ OSL_ENSURE( pParent, "No parent given." );
+ OSL_ENSURE( m_rThis.GetUpper(), "Where are we coming from?" );
+
+ // Be economical with notifications if an action is running.
+ SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+ const SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
+ const bool bComplete = pImp && pImp->IsAction() && pImp->GetLayAction().IsComplete();
+
+ if ( !bComplete )
+ {
+ SwFrame *pPre = m_rThis.GetIndPrev();
+ if ( pPre )
+ {
+ pPre->SetRetouche();
+ // follow-up of i#26250
+ // invalidate printing area of previous frame, if it's in a table
+ if ( pPre->GetUpper()->IsInTab() )
+ {
+ pPre->InvalidatePrt_();
+ }
+ pPre->InvalidatePage();
+ }
+ else
+ {
+ m_rThis.GetUpper()->SetCompletePaint();
+ m_rThis.GetUpper()->InvalidatePage();
+ }
+ }
+
+ SwPageFrame *pOldPage = m_rThis.FindPageFrame();
+
+ SwLayoutFrame *pOldParent;
+ bool bInvaLay;
+
+ {
+ //JoinLock pParent for the lifetime of the Cut/Paste call to avoid
+ //SwSectionFrame::MergeNext removing the pParent we're trying to reparent
+ //into
+ FlowFrameJoinLockGuard aJoinGuard(pParent);
+ SwFrameDeleteGuard aDeleteGuard(pParent);
+ pOldParent = CutTree( &m_rThis );
+ bInvaLay = PasteTree( &m_rThis, pParent, pSibling, pOldParent );
+ }
+
+ // If, by cutting & pasting, an empty SectionFrame came into existence, it should
+ // disappear automatically.
+ SwSectionFrame *pSct;
+
+ if ( pOldParent && !pOldParent->Lower() &&
+ ( pOldParent->IsInSct() &&
+ !(pSct = pOldParent->FindSctFrame())->ContainsContent() &&
+ !pSct->ContainsAny( true ) ) )
+ {
+ pSct->DelEmpty( false );
+ }
+
+ // If we're in a column section, we'd rather not call Calc "from below"
+ if( !m_rThis.IsInSct() &&
+ ( !m_rThis.IsInTab() || ( m_rThis.IsTabFrame() && !m_rThis.GetUpper()->IsInTab() ) ) )
+ m_rThis.GetUpper()->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+ else if( m_rThis.GetUpper()->IsSctFrame() )
+ {
+ SwSectionFrame* pTmpSct = static_cast<SwSectionFrame*>(m_rThis.GetUpper());
+ bool bOld = pTmpSct->IsContentLocked();
+ pTmpSct->SetContentLock( true );
+ pTmpSct->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+ if( !bOld )
+ pTmpSct->SetContentLock( false );
+ }
+ SwPageFrame *pPage = m_rThis.FindPageFrame();
+
+ if ( pOldPage != pPage )
+ {
+ m_rThis.InvalidatePage( pPage );
+ if ( m_rThis.IsLayoutFrame() )
+ {
+ SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(&m_rThis)->ContainsContent();
+ if ( pCnt )
+ pCnt->InvalidatePage( pPage );
+ }
+ else if ( pSh && pSh->GetDoc()->GetLineNumberInfo().IsRestartEachPage()
+ && pPage->FindFirstBodyContent() == &m_rThis )
+ {
+ m_rThis.InvalidateLineNum_();
+ }
+ }
+ if ( bInvaLay || (pSibling && pSibling->IsLayoutFrame()) )
+ m_rThis.GetUpper()->InvalidatePage( pPage );
+}
+
+bool SwFlowFrame::IsAnFollow( const SwFlowFrame *pAssumed ) const
+{
+ const SwFlowFrame *pFoll = this;
+ do
+ { if ( pAssumed == pFoll )
+ return true;
+ pFoll = pFoll->GetFollow();
+ } while ( pFoll );
+ return false;
+}
+
+SwTextFrame* SwContentFrame::FindMaster() const
+{
+ OSL_ENSURE( IsFollow(), "SwContentFrame::FindMaster(): !IsFollow" );
+
+ const SwContentFrame* pPrec = static_cast<const SwContentFrame*>(SwFlowFrame::GetPrecede());
+
+ if ( pPrec && pPrec->HasFollow() && pPrec->GetFollow() == this )
+ {
+ OSL_ENSURE( pPrec->IsTextFrame(), "NoTextFrame with follow found" );
+ return const_cast<SwTextFrame*>(static_cast< const SwTextFrame* >(pPrec));
+ }
+
+ OSL_FAIL( "Follow is lost in Space." );
+ return nullptr;
+}
+
+SwSectionFrame* SwSectionFrame::FindMaster() const
+{
+ OSL_ENSURE( IsFollow(), "SwSectionFrame::FindMaster(): !IsFollow" );
+
+ if (!m_pSection)
+ return nullptr;
+
+ SwIterator<SwSectionFrame,SwFormat> aIter( *m_pSection->GetFormat() );
+ SwSectionFrame* pSect = aIter.First();
+ while ( pSect )
+ {
+ if (pSect->GetFollow() == this)
+ return pSect;
+ pSect = aIter.Next();
+ }
+
+ OSL_FAIL( "Follow is lost in Space." );
+ return nullptr;
+}
+
+SwTabFrame* SwTabFrame::FindMaster( bool bFirstMaster ) const
+{
+ OSL_ENSURE( IsFollow(), "SwTabFrame::FindMaster(): !IsFollow" );
+
+ SwIterator<SwTabFrame,SwFormat> aIter( *GetTable()->GetFrameFormat() );
+ SwTabFrame* pTab = aIter.First();
+ while ( pTab )
+ {
+ if ( bFirstMaster )
+ {
+ // Optimization. This makes code like this obsolete:
+ // while ( pTab->IsFollow() )
+ // pTab = pTab->FindMaster();
+
+ if ( !pTab->IsFollow() )
+ {
+ SwTabFrame* pNxt = pTab;
+ while ( pNxt )
+ {
+ if ( pNxt->GetFollow() == this )
+ return pTab;
+ pNxt = pNxt->GetFollow();
+ }
+ }
+ }
+ else
+ {
+ if ( pTab->GetFollow() == this )
+ return pTab;
+ }
+
+ pTab = aIter.Next();
+ }
+
+ OSL_FAIL( "Follow is lost in Space." );
+ return nullptr;
+}
+
+/**
+ * Returns the next/previous Layout leaf that's NOT below this (or even is this itself).
+ * Also, that leaf must be in the same text flow as the pAnch origin frame (Body, Footnote)
+ */
+const SwLayoutFrame *SwFrame::GetLeaf( MakePageType eMakePage, bool bFwd,
+ const SwFrame *pAnch ) const
+{
+ // No flow, no joy...
+ if ( !(IsInDocBody() || IsInFootnote() || IsInFly()) )
+ return nullptr;
+
+ const SwFrame *pLeaf = this;
+ bool bFound = false;
+
+ do
+ { pLeaf = const_cast<SwFrame*>(pLeaf)->GetLeaf( eMakePage, bFwd );
+
+ if ( pLeaf &&
+ (!IsLayoutFrame() || !static_cast<const SwLayoutFrame*>(this)->IsAnLower( pLeaf )))
+ {
+ if ( pAnch->IsInDocBody() == pLeaf->IsInDocBody() &&
+ pAnch->IsInFootnote() == pLeaf->IsInFootnote() )
+ {
+ bFound = true;
+ }
+ }
+ } while ( !bFound && pLeaf );
+
+ return static_cast<const SwLayoutFrame*>(pLeaf);
+}
+
+SwLayoutFrame *SwFrame::GetLeaf( MakePageType eMakePage, bool bFwd )
+{
+ if ( IsInFootnote() )
+ return bFwd ? GetNextFootnoteLeaf( eMakePage ) : GetPrevFootnoteLeaf( eMakePage );
+
+ // i#53323
+ // A frame could be inside a table AND inside a section.
+ // Thus, it has to be determined, which is the first parent.
+ bool bInTab( IsInTab() );
+ bool bInSct( IsInSct() );
+ if ( bInTab && bInSct )
+ {
+ const SwFrame* pUpperFrame( GetUpper() );
+ while ( pUpperFrame )
+ {
+ if ( pUpperFrame->IsTabFrame() )
+ {
+ // the table is the first.
+ bInSct = false;
+ break;
+ }
+ else if ( pUpperFrame->IsSctFrame() )
+ {
+ // the section is the first.
+ bInTab = false;
+ break;
+ }
+
+ pUpperFrame = pUpperFrame->GetUpper();
+ }
+ }
+
+ if ( bInTab && ( !IsTabFrame() || GetUpper()->IsCellFrame() ) ) // TABLE IN TABLE
+ return bFwd ? GetNextCellLeaf() : GetPrevCellLeaf();
+
+ if ( bInSct )
+ return bFwd ? GetNextSctLeaf( eMakePage ) : GetPrevSctLeaf();
+
+ return bFwd ? GetNextLeaf( eMakePage ) : GetPrevLeaf();
+}
+
+bool SwFrame::WrongPageDesc( SwPageFrame* pNew )
+{
+ // Now it's getting a bit complicated:
+
+ // Maybe I'm bringing a Pagedesc myself; in that case,
+ // the pagedesc of the next page needs to correspond.
+ // Otherwise, I'll have to dig a bit deeper to see where
+ // the following Pagedesc is coming from.
+ // If the following page itself tells me that it's pagedesc
+ // is wrong, I can happily exchange it.
+ // If the page however thinks that it's pagedesc is correct,
+ // this doesn't mean it's useful to me:
+ // If the first BodyContent asks for a PageDesc or a PageBreak,
+ // I'll have to insert a new page - except the desired page is
+ // the correct one.
+ // If I inserted a new page, the problems only get started:
+ // because then it's likely for the next page to have been
+ // wrong and having been swapped because of that.
+ // This in turn means that I have a new (and correct) page,
+ // but the conditions to swap still apply.
+ // Way out of the situation: Try to preliminarily insert a
+ // new page once (empty pages are already inserted by InsertPage()
+ // if required)
+
+ //My Pagedesc doesn't count if I'm a follow!
+ const SwPageDesc *pDesc = nullptr;
+ std::optional<sal_uInt16> oTmp;
+ SwFlowFrame *pFlow = SwFlowFrame::CastFlowFrame( this );
+ if ( !pFlow || !pFlow->IsFollow() )
+ {
+ const SwFormatPageDesc &rFormatDesc = GetPageDescItem();
+ pDesc = rFormatDesc.GetPageDesc();
+ if( pDesc )
+ {
+ if( !pDesc->GetRightFormat() )
+ oTmp = 2;
+ else if( !pDesc->GetLeftFormat() )
+ oTmp = 1;
+ else if( rFormatDesc.GetNumOffset() )
+ oTmp = rFormatDesc.GetNumOffset();
+ }
+ }
+
+ // Does the Content bring a Pagedesc or do we need the
+ // virtual page number of the new layout leaf?
+ // PageDesc isn't allowed with Follows
+ const bool isRightPage = oTmp ? sw::IsRightPageByNumber(*mpRoot, *oTmp) : pNew->OnRightPage();
+ if ( !pDesc )
+ pDesc = pNew->FindPageDesc();
+
+ bool bFirst = pNew->OnFirstPage();
+
+ const SwFlowFrame *pNewFlow = pNew->FindFirstBodyContent();
+ // Did we find ourselves?
+ if( pNewFlow == pFlow )
+ pNewFlow = nullptr;
+ if ( pNewFlow && pNewFlow->GetFrame().IsInTab() )
+ pNewFlow = pNewFlow->GetFrame().FindTabFrame();
+ const SwPageDesc *pNewDesc= ( pNewFlow && !pNewFlow->IsFollow() )
+ ? pNewFlow->GetFrame().GetPageDescItem().GetPageDesc()
+ : nullptr;
+
+ SAL_INFO( "sw.pageframe", "WrongPageDesc p: " << pNew << " phys: " << pNew->GetPhyPageNum() );
+ SAL_INFO( "sw.pageframe", "WrongPageDesc " << pNew->GetPageDesc() << " " << pDesc );
+ SAL_INFO( "sw.pageframe", "WrongPageDesc right: " << isRightPage
+ << " first: " << bFirst << " " << pNew->GetFormat() << " == "
+ << (isRightPage ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst)) << " "
+ << (isRightPage ? pDesc->GetLeftFormat(bFirst) : pDesc->GetRightFormat(bFirst)) );
+
+ return (pNew->GetPageDesc() != pDesc) // own desc ?
+ || (pNew->GetFormat() !=
+ (isRightPage ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst)))
+ || (pNewDesc && pNewDesc == pDesc);
+}
+
+/// Returns the next layout leaf in which we can move the frame.
+SwLayoutFrame *SwFrame::GetNextLeaf( MakePageType eMakePage )
+{
+ OSL_ENSURE( !IsInFootnote(), "GetNextLeaf(), don't call me for Footnote." );
+ OSL_ENSURE( !IsInSct(), "GetNextLeaf(), don't call me for Sections." );
+
+ const bool bBody = IsInDocBody(); // If I'm coming from the DocBody,
+ // I want to end up in the body.
+
+ // It doesn't make sense to insert pages, as we only want to search the
+ // chain.
+ if( IsInFly() )
+ eMakePage = MAKEPAGE_NONE;
+
+ // For tables, we just take the big leap. A simple GetNext would
+ // iterate through the first cells and, in turn, all other cells.
+ SwLayoutFrame *pLayLeaf = nullptr;
+ if ( IsTabFrame() )
+ {
+ SwFrame *const pTmp = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
+ if ( pTmp )
+ pLayLeaf = pTmp->GetUpper();
+ }
+ if ( !pLayLeaf )
+ pLayLeaf = GetNextLayoutLeaf();
+
+ SwLayoutFrame *pOldLayLeaf = nullptr; // Make sure that we don't have to
+ // start searching from top when we
+ // have a freshly created page.
+ bool bNewPg = false; // Only insert a new page once.
+
+ while ( true )
+ {
+ if ( pLayLeaf )
+ {
+ // There's yet another LayoutFrame. Let's see if it's ready to host
+ // me as well.
+ // It only needs to be of the same kind like my starting point
+ // (DocBody or Footnote respectively)
+ if ( pLayLeaf->FindPageFrame()->IsFootnotePage() )
+ { // If I ended up at the end note pages, we're done.
+ pLayLeaf = nullptr;
+ continue;
+ }
+ if ( (bBody && !pLayLeaf->IsInDocBody()) || pLayLeaf->IsInTab()
+ || pLayLeaf->IsInSct() )
+ {
+ // They don't want me! Try again
+ pOldLayLeaf = pLayLeaf;
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ continue;
+ }
+
+ // I'm wanted, therefore I'm done. However, it may still be that,
+ // during a page break, the page type isn't the desired one. In that
+ // case we have to insert a page of the correct type.
+
+ if( !IsFlowFrame() && ( eMakePage == MAKEPAGE_NONE ||
+ eMakePage==MAKEPAGE_APPEND || eMakePage==MAKEPAGE_NOSECTION ) )
+ return pLayLeaf;
+
+ SwPageFrame *pNew = pLayLeaf->FindPageFrame();
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ // The pagedesc check does not make sense for frames in fly frames
+ if ( pNew != FindPageFrame() && !bNewPg && !IsInFly() &&
+ // i#46683
+ // Do not consider page descriptions in browse mode (since
+ // MoveBwd ignored them)
+ !(pSh && pSh->GetViewOptions()->getBrowseMode() ) )
+ {
+ if( WrongPageDesc( pNew ) )
+ {
+ SwFootnoteContFrame *pCont = pNew->FindFootnoteCont();
+ if( pCont )
+ {
+ // If the reference of the first footnote of this page
+ // lies before the page, we'd rather not insert a new page.
+
+ SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ if( pFootnote && pFootnote->GetRef() )
+ {
+ const sal_uInt16 nRefNum = pNew->GetPhyPageNum();
+ if( pFootnote->GetRef()->GetPhyPageNum() < nRefNum )
+ break;
+ }
+ }
+ //Gotcha! The following page is wrong, therefore we need to
+ //insert a new one.
+ if ( eMakePage == MAKEPAGE_INSERT )
+ {
+ bNewPg = true;
+
+ SwPageFrame *pPg = pOldLayLeaf ?
+ pOldLayLeaf->FindPageFrame() : nullptr;
+ if ( pPg && pPg->IsEmptyPage() )
+ // Don't insert behind. Insert before the EmptyPage.
+ pPg = static_cast<SwPageFrame*>(pPg->GetPrev());
+
+ if ( !pPg || pPg == pNew )
+ pPg = FindPageFrame();
+
+ InsertPage( pPg, false );
+ pLayLeaf = GetNextLayoutLeaf();
+ pOldLayLeaf = nullptr;
+ continue;
+ }
+ else
+ pLayLeaf = nullptr;
+ }
+ }
+ break;
+ }
+ else
+ {
+ // There's no other matching LayoutFrame, so we have to insert
+ // a new page.
+ if ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT )
+ {
+ InsertPage(
+ pOldLayLeaf ? pOldLayLeaf->FindPageFrame() : FindPageFrame(),
+ false );
+
+ // And again from the start.
+ pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf();
+ }
+ else
+ break;
+ }
+ }
+ return pLayLeaf;
+}
+
+/// Returns the previous layout leaf where we can move the frame.
+SwLayoutFrame *SwFrame::GetPrevLeaf()
+{
+ OSL_ENSURE( !IsInFootnote(), "GetPrevLeaf(), don't call me for Footnote." );
+
+ const bool bBody = IsInDocBody(); // If I'm coming from the DocBody,
+ // I want to end up in the body.
+ const bool bFly = IsInFly();
+
+ SwLayoutFrame *pLayLeaf = GetPrevLayoutLeaf();
+ SwLayoutFrame *pPrevLeaf = nullptr;
+
+ while ( pLayLeaf )
+ {
+ if ( pLayLeaf->IsInTab() || // Never go into tables.
+ pLayLeaf->IsInSct() ) // Same goes for sections!
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ else if ( bBody && pLayLeaf->IsInDocBody() )
+ {
+ if ( pLayLeaf->Lower() )
+ break;
+ pPrevLeaf = pLayLeaf;
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ if ( pLayLeaf )
+ SwFlowFrame::SetMoveBwdJump( true );
+ }
+ else if ( bFly )
+ break; //Contents in Flys should accept any layout leaf.
+ else
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ }
+ return pLayLeaf ? pLayLeaf : pPrevLeaf;
+}
+
+bool SwFlowFrame::IsPrevObjMove() const
+{
+ // true: The FlowFrame must respect the a border of the predecessor, also needs
+ // to insert a break if required.
+
+ //!!!!!!!!!!!Hack!!!!!!!!!!!
+ const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ return false;
+
+ SwFrame *pPre = m_rThis.FindPrev();
+
+ if ( pPre && pPre->GetDrawObjs() )
+ {
+ OSL_ENSURE( SwFlowFrame::CastFlowFrame( pPre ), "new flowfrm?" );
+ if( SwFlowFrame::CastFlowFrame( pPre )->IsAnFollow( this ) )
+ return false;
+ if (SwFlowFrame::CastFlowFrame(pPre)->IsJoinLocked())
+ {
+ SwBorderAttrAccess baa(SwFrame::GetCache(), pPre);
+ SwBorderAttrs const& rAttrs(*baa.Get());
+ if (SwFlowFrame::CastFlowFrame(pPre)->IsKeep(rAttrs.GetAttrSet().GetKeep(), pPre->GetBreakItem()))
+ { // pPre is currently being formatted - maybe it moved back but
+ // its objects still have the old page's body as
+ // mpVertPosOrientFrame and SwContentFrame::MakeAll() is calling
+ // pNxt->Calc() in this case so allow this frame to move back
+ return false; // too, else pPre is forced to move forward again.
+ }
+ }
+ SwLayoutFrame* pPreUp = pPre->GetUpper();
+ // If the upper is a SectionFrame, or a column of a SectionFrame, we're
+ // allowed to protrude out of it. However, we need to respect the
+ // Upper of the SectionFrame.
+ if( pPreUp->IsInSct() )
+ {
+ if( pPreUp->IsSctFrame() )
+ pPreUp = pPreUp->GetUpper();
+ else if( pPreUp->IsColBodyFrame() &&
+ pPreUp->GetUpper()->GetUpper()->IsSctFrame() )
+ pPreUp = pPreUp->GetUpper()->GetUpper()->GetUpper();
+ }
+ // i#26945 - re-factoring
+ // use <GetVertPosOrientFrame()> to determine, if object has followed the
+ // text flow to the next layout frame
+ for (SwAnchoredObject* pObj : *pPre->GetDrawObjs())
+ {
+
+ // Do not consider hidden objects
+ // i#26945 - do not consider object, which
+ // doesn't follow the text flow.
+ if ( pObj->GetFrameFormat().GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
+ pObj->GetDrawObj()->GetLayer() ) &&
+ pObj->GetFrameFormat().GetFollowTextFlow().GetValue() )
+ {
+ const SwLayoutFrame* pVertPosOrientFrame = pObj->GetVertPosOrientFrame();
+ if ( pVertPosOrientFrame &&
+ pPreUp != pVertPosOrientFrame &&
+ !pPreUp->IsAnLower( pVertPosOrientFrame ) )
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+|* If there's a hard page break before the Frame AND there's a
+|* predecessor on the same page, true is returned (we need to create a
+|* new PageBreak). Otherwise, returns false.
+|* If bAct is set to true, this function returns true if
+|* there's a PageBreak.
+|* Of course, we don't evaluate the hard page break for follows.
+|* The page break is in its own FrameFormat (BEFORE) or in the FrameFormat of the
+|* predecessor (AFTER). If there's no predecessor on the page, we don't
+|* need to think further.
+|* Also, a page break (or the need for one) is also present if
+|* the FrameFormat contains a PageDesc.
+|* The implementation works only on ContentFrames! - the definition
+|* of the predecessor is not clear for LayoutFrames.
+|*/
+bool SwFlowFrame::IsPageBreak( bool bAct ) const
+{
+ if ( !IsFollow() && m_rThis.IsInDocBody() &&
+ ( !m_rThis.IsInTab() || ( m_rThis.IsTabFrame() && !m_rThis.GetUpper()->IsInTab() ) ) ) // i66968
+ {
+ const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ return false;
+
+ // Determine predecessor
+ const SwFrame *pPrev = m_rThis.FindPrev();
+ while ( pPrev && ( !pPrev->IsInDocBody() ||
+ ( pPrev->IsTextFrame() && static_cast<const SwTextFrame*>(pPrev)->IsHiddenNow() ) ) )
+ pPrev = pPrev->FindPrev();
+
+ if ( pPrev )
+ {
+ OSL_ENSURE( pPrev->IsInDocBody(), "IsPageBreak: Not in DocBody?" );
+ if ( bAct )
+ { if ( m_rThis.FindPageFrame() == pPrev->FindPageFrame() )
+ return false;
+ }
+ else
+ { if ( m_rThis.FindPageFrame() != pPrev->FindPageFrame() )
+ return false;
+ }
+
+ //for compatibility, also break at column break if no columns exist
+ const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
+ const bool bTreatSingleColumnBreakAsPageBreak = rIDSA.get(DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK);
+ const SvxBreak eBreak = m_rThis.GetBreakItem().GetBreak();
+ if ( eBreak == SvxBreak::PageBefore ||
+ eBreak == SvxBreak::PageBoth ||
+ ( bTreatSingleColumnBreakAsPageBreak && eBreak == SvxBreak::ColumnBefore && !m_rThis.FindColFrame() ))
+ return true;
+ else
+ {
+ const SvxBreak &ePrB = pPrev->GetBreakItem().GetBreak();
+ if ( ePrB == SvxBreak::PageAfter ||
+ ePrB == SvxBreak::PageBoth ||
+ m_rThis.GetPageDescItem().GetPageDesc())
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+|* If there's a hard column break before the Frame AND there is
+|* a predecessor in the same column, we return true (we need to create
+|* a ColBreak). Otherwise, we return false.
+|* If bAct is set to true, we return true if there's a ColBreak.
+|* Of course, we don't evaluate the hard column break for follows.
+|*
+|* The column break is in its own FrameFormat (BEFORE) or in the FrameFormat of the
+|* predecessor (AFTER). If there's no predecessor in the column, we don't
+|* need to think further.
+|* The implementation works only on ContentFrames! - the definition
+|* of the predecessor is not clear for LayoutFrames.
+|*/
+bool SwFlowFrame::IsColBreak( bool bAct ) const
+{
+ if ( !IsFollow() && (m_rThis.IsMoveable() || bAct) )
+ {
+ const SwFrame *pCol = m_rThis.FindColFrame();
+ if ( pCol )
+ {
+ // Determine predecessor
+ const SwFrame *pPrev = m_rThis.FindPrev();
+ while( pPrev && ( ( !pPrev->IsInDocBody() && !m_rThis.IsInFly() && !m_rThis.FindFooterOrHeader() ) ||
+ ( pPrev->IsTextFrame() && static_cast<const SwTextFrame*>(pPrev)->IsHiddenNow() ) ) )
+ pPrev = pPrev->FindPrev();
+
+ if ( pPrev )
+ {
+ if ( bAct )
+ { if ( pCol == pPrev->FindColFrame() )
+ return false;
+ }
+ else
+ { if ( pCol != pPrev->FindColFrame() )
+ return false;
+ }
+
+ const SvxBreak eBreak = m_rThis.GetBreakItem().GetBreak();
+ if ( eBreak == SvxBreak::ColumnBefore ||
+ eBreak == SvxBreak::ColumnBoth )
+ return true;
+ else
+ {
+ const SvxBreak &ePrB = pPrev->GetBreakItem().GetBreak();
+ if ( ePrB == SvxBreak::ColumnAfter ||
+ ePrB == SvxBreak::ColumnBoth )
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const
+{
+ if( m_rThis.IsInSct() )
+ {
+ const SwFrame* pTmp = m_rThis.GetUpper();
+ while( pTmp )
+ {
+ if( pTmp->IsCellFrame() || pTmp->IsFlyFrame() ||
+ pTmp->IsFooterFrame() || pTmp->IsHeaderFrame() ||
+ ( pTmp->IsFootnoteFrame() && !static_cast<const SwFootnoteFrame*>(pTmp)->GetMaster() ) )
+ return true;
+ if( pTmp->IsPageFrame() )
+ return !pTmp->GetPrev() || IsPageBreak(true);
+ if( pTmp->IsColumnFrame() && pTmp->GetPrev() )
+ return IsColBreak( true );
+ if( pTmp->IsSctFrame() && ( !bSct || pTmp->GetPrev() ) )
+ return false;
+ pTmp = pTmp->GetUpper();
+ }
+ OSL_FAIL( "HasParaSpaceAtPages: Where's my page?" );
+ return false;
+ }
+ if( !m_rThis.IsInDocBody() || ( m_rThis.IsInTab() && !m_rThis.IsTabFrame()) ||
+ IsPageBreak( true ) || ( m_rThis.FindColFrame() && IsColBreak( true ) ) )
+ return true;
+ const SwFrame* pTmp = m_rThis.FindColFrame();
+ if( pTmp )
+ {
+ if( pTmp->GetPrev() )
+ return false;
+ }
+ else
+ pTmp = &m_rThis;
+ pTmp = pTmp->FindPageFrame();
+ return pTmp && !pTmp->GetPrev();
+}
+
+/** helper method to determine previous frame for calculation of the
+ upper space
+
+ i#11860
+*/
+const SwFrame* SwFlowFrame::GetPrevFrameForUpperSpaceCalc_( const SwFrame* _pProposedPrevFrame ) const
+{
+ const SwFrame* pPrevFrame = _pProposedPrevFrame
+ ? _pProposedPrevFrame
+ : m_rThis.GetPrev();
+
+ // Skip hidden paragraphs and empty sections
+ while ( pPrevFrame &&
+ ( ( pPrevFrame->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) ||
+ ( pPrevFrame->IsSctFrame() &&
+ !static_cast<const SwSectionFrame*>(pPrevFrame)->GetSection() ) ) )
+ {
+ pPrevFrame = pPrevFrame->GetPrev();
+ }
+
+ // Special case: no direct previous frame is found but frame is in footnote
+ // Search for a previous frame in previous footnote,
+ // if frame isn't in a section, which is also in the footnote
+ if ( !pPrevFrame && m_rThis.IsInFootnote() &&
+ ( m_rThis.IsSctFrame() ||
+ !m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsInFootnote() ) )
+ {
+ const SwFootnoteFrame* pPrevFootnoteFrame =
+ static_cast<const SwFootnoteFrame*>(m_rThis.FindFootnoteFrame()->GetPrev());
+ if ( pPrevFootnoteFrame )
+ {
+ pPrevFrame = pPrevFootnoteFrame->GetLastLower();
+
+ // Skip hidden paragraphs and empty sections
+ while ( pPrevFrame &&
+ ( ( pPrevFrame->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) ||
+ ( pPrevFrame->IsSctFrame() &&
+ !static_cast<const SwSectionFrame*>(pPrevFrame)->GetSection() ) ) )
+ {
+ pPrevFrame = pPrevFrame->GetPrev();
+ }
+ }
+ }
+ // Special case: found previous frame is a section
+ // Search for the last content in the section
+ if( pPrevFrame && pPrevFrame->IsSctFrame() )
+ {
+ const SwSectionFrame* pPrevSectFrame =
+ static_cast<const SwSectionFrame*>(pPrevFrame);
+ pPrevFrame = pPrevSectFrame->FindLastContent();
+ // If the last content is in a table _inside_ the section,
+ // take the table herself.
+ // Correction: Check directly, if table is inside table, instead of indirectly
+ // by checking, if section isn't inside a table
+ if ( pPrevFrame && pPrevFrame->IsInTab() )
+ {
+ const SwTabFrame* pTableFrame = pPrevFrame->FindTabFrame();
+ if ( pPrevSectFrame->IsAnLower( pTableFrame ) )
+ {
+ pPrevFrame = pTableFrame;
+ }
+ }
+ // Correction: skip hidden text frames
+ while ( pPrevFrame &&
+ pPrevFrame->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() )
+ {
+ pPrevFrame = pPrevFrame->GetPrev();
+ }
+ }
+
+ return pPrevFrame;
+}
+
+/// Compare styles attached to these text frames.
+static bool lcl_IdenticalStyles(const SwFrame* pPrevFrame, const SwFrame* pFrame)
+{
+ SwTextFormatColl *pPrevFormatColl = nullptr;
+ if (pPrevFrame && pPrevFrame->IsTextFrame())
+ {
+ const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pPrevFrame );
+ pPrevFormatColl = dynamic_cast<SwTextFormatColl*>(
+ pTextFrame->GetTextNodeForParaProps()->GetFormatColl());
+ }
+
+ bool bIdenticalStyles = false;
+ if (pFrame && pFrame->IsTextFrame())
+ {
+ const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pFrame );
+ SwTextFormatColl *const pFormatColl = dynamic_cast<SwTextFormatColl*>(
+ pTextFrame->GetTextNodeForParaProps()->GetFormatColl());
+ bIdenticalStyles = pPrevFormatColl == pFormatColl;
+ }
+ return bIdenticalStyles;
+}
+
+static bool lcl_getContextualSpacing(const SwFrame* pPrevFrame)
+{
+ bool bRet;
+ SwBorderAttrAccess aAccess(SwFrame::GetCache(), pPrevFrame);
+ const SwBorderAttrs *pAttrs = aAccess.Get();
+
+ bRet = pAttrs->GetULSpace().GetContext();
+
+ return bRet;
+}
+
+
+SwTwips SwFlowFrame::CalcUpperSpace( const SwBorderAttrs *pAttrs,
+ const SwFrame* pPr,
+ const bool _bConsiderGrid ) const
+{
+ const SwFrame* pPrevFrame = GetPrevFrameForUpperSpaceCalc_( pPr );
+
+ std::optional<SwBorderAttrAccess> oAccess;
+ SwFrame* pOwn;
+ if( !pAttrs )
+ {
+ if( m_rThis.IsSctFrame() )
+ {
+ SwSectionFrame* pFoll = &static_cast<SwSectionFrame&>(m_rThis);
+ do
+ pOwn = pFoll->ContainsAny();
+ while( !pOwn && nullptr != ( pFoll = pFoll->GetFollow() ) );
+ if( !pOwn )
+ return 0;
+ }
+ else
+ pOwn = &m_rThis;
+ oAccess.emplace(SwFrame::GetCache(), pOwn);
+ pAttrs = oAccess->Get();
+ }
+ else
+ {
+ pOwn = &m_rThis;
+ }
+ SwTwips nUpper = 0;
+
+ {
+ const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
+ if( pPrevFrame )
+ {
+ const bool bUseFormerLineSpacing = rIDSA.get(DocumentSettingId::OLD_LINE_SPACING);
+ const bool bContextualSpacingThis = pAttrs->GetULSpace().GetContext();
+ const bool bContextualSpacingPrev = lcl_getContextualSpacing(pPrevFrame);
+ bool bIdenticalStyles = lcl_IdenticalStyles(pPrevFrame, &m_rThis);
+
+ const bool bContextualSpacing = bContextualSpacingThis
+ && bContextualSpacingPrev
+ && bIdenticalStyles;
+
+ // tdf#125893 always ignore own top margin setting of the actual paragraph
+ // with contextual spacing, if the previous paragraph is identical
+ const bool bHalfContextualSpacing = !bContextualSpacing
+ && bContextualSpacingThis
+ && !bContextualSpacingPrev
+ && bIdenticalStyles;
+
+ // tdf#134463 always ignore own bottom margin setting of the previous paragraph
+ // with contextual spacing, if the actual paragraph is identical
+ const bool bHalfContextualSpacingPrev = !bContextualSpacing
+ && !bContextualSpacingThis
+ && bContextualSpacingPrev
+ && bIdenticalStyles;
+
+ // i#11860 - use new method to determine needed spacing
+ // values of found previous frame and use these values.
+ SwTwips nPrevLowerSpace = 0;
+ SwTwips nPrevLineSpacing = 0;
+ // i#102458
+ bool bPrevLineSpacingProportional = false;
+ GetSpacingValuesOfFrame( (*pPrevFrame),
+ nPrevLowerSpace, nPrevLineSpacing,
+ bPrevLineSpacingProportional,
+ bIdenticalStyles);
+ if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) )
+ {
+ // FIXME: apply bHalfContextualSpacing for better portability?
+ nUpper = bContextualSpacing ? 0 : nPrevLowerSpace + pAttrs->GetULSpace().GetUpper();
+ SwTwips nAdd = nPrevLineSpacing;
+ // i#11859 - consideration of the line spacing
+ // for the upper spacing of a text frame
+ if ( bUseFormerLineSpacing )
+ {
+ // former consideration
+ if ( pOwn->IsTextFrame() )
+ {
+ nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace()) );
+ }
+ nUpper += nAdd;
+ }
+ else
+ {
+ // new consideration:
+ // Only the proportional line spacing of the previous
+ // text frame is considered for the upper spacing and
+ // the line spacing values are add up instead of
+ // building its maximum.
+ if ( pOwn->IsTextFrame() )
+ {
+ // i#102458
+ // Correction:
+ // A proportional line spacing of the previous text frame
+ // is added up to an own leading line spacing.
+ // Otherwise, the maximum of the leading line spacing
+ // of the previous text frame and the own leading line
+ // spacing is built.
+ if ( bPrevLineSpacingProportional )
+ {
+ nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true );
+ }
+ else
+ {
+ nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true )) );
+ }
+ }
+ nUpper += nAdd;
+ }
+ }
+ else
+ {
+ nUpper = bContextualSpacing ? 0 : std::max(
+ bHalfContextualSpacingPrev ? 0 : static_cast<tools::Long>(nPrevLowerSpace),
+ bHalfContextualSpacing ? 0 : static_cast<tools::Long>(pAttrs->GetULSpace().GetUpper()) );
+
+ // i#11859 - consideration of the line spacing
+ // for the upper spacing of a text frame
+ if ( bUseFormerLineSpacing )
+ {
+ // former consideration
+ if ( pOwn->IsTextFrame() )
+ nUpper = std::max( nUpper, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace()) );
+ if ( nPrevLineSpacing != 0 )
+ {
+ nUpper = std::max( nUpper, nPrevLineSpacing );
+ }
+ }
+ else
+ {
+ // new consideration:
+ // Only the proportional line spacing of the previous
+ // text frame is considered for the upper spacing and
+ // the line spacing values are add up and added to
+ // the paragraph spacing instead of building the
+ // maximum of the line spacings and the paragraph spacing.
+ SwTwips nAdd = nPrevLineSpacing;
+ if ( pOwn->IsTextFrame() )
+ {
+ // i#102458
+ // Correction:
+ // A proportional line spacing of the previous text frame
+ // is added up to an own leading line spacing.
+ // Otherwise, the maximum of the leading line spacing
+ // of the previous text frame and the own leading line
+ // spacing is built.
+ if ( bPrevLineSpacingProportional )
+ {
+ nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true );
+ }
+ else
+ {
+ nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true )) );
+ }
+ }
+ nUpper += nAdd;
+ }
+ }
+ }
+ else if ( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) &&
+ CastFlowFrame( pOwn )->HasParaSpaceAtPages( m_rThis.IsSctFrame() ) )
+ {
+ nUpper = pAttrs->GetULSpace().GetUpper();
+ }
+ }
+
+ // i#25029 - pass previous frame <pPrevFrame>
+ // to method <GetTopLine(..)>, if parameter <pPr> is set.
+ // Note: parameter <pPr> is set, if method is called from <SwTextFrame::WouldFit(..)>
+ nUpper += pAttrs->GetTopLine( m_rThis, (pPr ? pPrevFrame : nullptr) );
+
+ // i#11860 - consider value of new parameter <_bConsiderGrid>
+ // and use new method <GetUpperSpaceAmountConsideredForPageGrid(..)>
+
+ //consider grid in square page mode
+ if ( _bConsiderGrid && m_rThis.GetUpper()->GetFormat()->GetDoc()->IsSquaredPageMode() )
+ {
+ nUpper += GetUpperSpaceAmountConsideredForPageGrid_( nUpper );
+ }
+ return nUpper;
+}
+
+/** method to determine the upper space amount, which is considered for
+ the page grid
+
+ i#11860
+ Precondition: Position of frame is valid.
+*/
+SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPageGrid_(
+ const SwTwips _nUpperSpaceWithoutGrid ) const
+{
+ SwTwips nUpperSpaceAmountConsideredForPageGrid = 0;
+
+ if ( m_rThis.IsInDocBody() && m_rThis.GetAttrSet()->GetParaGrid().GetValue() )
+ {
+ const SwPageFrame* pPageFrame = m_rThis.FindPageFrame();
+ SwTextGridItem const*const pGrid(GetGridItem(pPageFrame));
+ if( pGrid )
+ {
+ const SwFrame* pBodyFrame = pPageFrame->FindBodyCont();
+ if ( pBodyFrame )
+ {
+ const tools::Long nGridLineHeight =
+ pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
+
+ SwRectFnSet aRectFnSet(&m_rThis);
+ const SwTwips nBodyPrtTop = aRectFnSet.GetPrtTop(*pBodyFrame);
+ const SwTwips nProposedPrtTop =
+ aRectFnSet.YInc( aRectFnSet.GetTop(m_rThis.getFrameArea()),
+ _nUpperSpaceWithoutGrid );
+
+ const SwTwips nSpaceAbovePrtTop =
+ aRectFnSet.YDiff( nProposedPrtTop, nBodyPrtTop );
+ const SwTwips nSpaceOfCompleteLinesAbove =
+ nGridLineHeight * ( nSpaceAbovePrtTop / nGridLineHeight );
+ SwTwips nNewPrtTop =
+ aRectFnSet.YInc( nBodyPrtTop, nSpaceOfCompleteLinesAbove );
+ if ( aRectFnSet.YDiff( nProposedPrtTop, nNewPrtTop ) > 0 )
+ {
+ nNewPrtTop = aRectFnSet.YInc( nNewPrtTop, nGridLineHeight );
+ }
+
+ const SwTwips nNewUpperSpace =
+ aRectFnSet.YDiff( nNewPrtTop,
+ aRectFnSet.GetTop(m_rThis.getFrameArea()) );
+
+ nUpperSpaceAmountConsideredForPageGrid =
+ nNewUpperSpace - _nUpperSpaceWithoutGrid;
+
+ OSL_ENSURE( nUpperSpaceAmountConsideredForPageGrid >= 0,
+ "<SwFlowFrame::GetUpperSpaceAmountConsideredForPageGrid(..)> - negative space considered for page grid!" );
+ }
+ }
+ }
+ return nUpperSpaceAmountConsideredForPageGrid;
+}
+
+/** method to determine the upper space amount, which is considered for
+ the previous frame
+
+ i#11860
+*/
+SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPrevFrame() const
+{
+ SwTwips nUpperSpaceAmountOfPrevFrame = 0;
+
+ const SwFrame* pPrevFrame = GetPrevFrameForUpperSpaceCalc_();
+ if ( pPrevFrame )
+ {
+ SwTwips nPrevLowerSpace = 0;
+ SwTwips nPrevLineSpacing = 0;
+ // i#102458
+ bool bDummy = false;
+ GetSpacingValuesOfFrame( (*pPrevFrame), nPrevLowerSpace, nPrevLineSpacing, bDummy, lcl_IdenticalStyles(pPrevFrame, &m_rThis));
+ if ( nPrevLowerSpace > 0 || nPrevLineSpacing > 0 )
+ {
+ const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
+ if ( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) ||
+ !rIDSA.get(DocumentSettingId::OLD_LINE_SPACING) )
+ {
+ nUpperSpaceAmountOfPrevFrame = nPrevLowerSpace + nPrevLineSpacing;
+ }
+ else
+ {
+ nUpperSpaceAmountOfPrevFrame = std::max( nPrevLowerSpace, nPrevLineSpacing );
+ }
+ }
+ }
+
+ return nUpperSpaceAmountOfPrevFrame;
+}
+
+/** method to determine the upper space amount, which is considered for
+ the previous frame and the page grid, if option 'Use former object
+ positioning' is OFF
+
+ i#11860
+*/
+SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid() const
+{
+ SwTwips nUpperSpaceAmountConsideredForPrevFrameAndPageGrid = 0;
+
+ if (!m_rThis.GetUpper() || !m_rThis.GetUpper()->GetFormat())
+ {
+ return nUpperSpaceAmountConsideredForPrevFrameAndPageGrid;
+ }
+
+ if ( !m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_OBJECT_POS) )
+ {
+ nUpperSpaceAmountConsideredForPrevFrameAndPageGrid =
+ GetUpperSpaceAmountConsideredForPrevFrame() +
+ ( m_rThis.GetUpper()->GetFormat()->GetDoc()->IsSquaredPageMode()
+ ? GetUpperSpaceAmountConsideredForPageGrid_( CalcUpperSpace( nullptr, nullptr, false ) )
+ : 0 );
+ }
+
+ return nUpperSpaceAmountConsideredForPrevFrameAndPageGrid;
+}
+
+// Calculation of lower space
+
+SwTwips SwFlowFrame::CalcLowerSpace( const SwBorderAttrs* _pAttrs ) const
+{
+ SwTwips nLowerSpace = 0;
+
+ std::optional<SwBorderAttrAccess> oAttrAccess;
+ if ( !_pAttrs )
+ {
+ oAttrAccess.emplace(SwFrame::GetCache(), &m_rThis);
+ _pAttrs = oAttrAccess->Get();
+ }
+
+ bool bCommonBorder = true;
+ if ( m_rThis.IsInSct() && m_rThis.GetUpper()->IsColBodyFrame() )
+ {
+ const SwSectionFrame* pSectFrame = m_rThis.FindSctFrame();
+ bCommonBorder = pSectFrame->GetFormat()->GetBalancedColumns().GetValue();
+ }
+ nLowerSpace = bCommonBorder ?
+ _pAttrs->GetBottomLine( m_rThis ) :
+ _pAttrs->CalcBottomLine();
+
+ // i#26250
+ // - correct consideration of table frames
+ // - use new method <CalcAddLowerSpaceAsLastInTableCell(..)>
+ if ( ( ( m_rThis.IsTabFrame() && m_rThis.GetUpper()->IsInTab() ) ||
+ // No lower spacing, if frame has a follow
+ ( m_rThis.IsInTab() && !GetFollow() ) ) &&
+ !m_rThis.GetIndNext() )
+ {
+ nLowerSpace += CalcAddLowerSpaceAsLastInTableCell( _pAttrs );
+ }
+
+ // tdf#128195 Consider para spacing below last paragraph in header
+ bool bHasSpacingBelowPara = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess().get(
+ DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA);
+ if (bHasSpacingBelowPara && !m_rThis.IsInFly() && m_rThis.FindFooterOrHeader() && !GetFollow()
+ && !m_rThis.GetIndNext())
+ nLowerSpace += _pAttrs->GetULSpace().GetLower() + _pAttrs->CalcLineSpacing();
+
+ return nLowerSpace;
+}
+
+/** calculation of the additional space to be considered, if flow frame
+ is the last inside a table cell
+
+ i#26250
+*/
+SwTwips SwFlowFrame::CalcAddLowerSpaceAsLastInTableCell(
+ const SwBorderAttrs* _pAttrs ) const
+{
+ SwTwips nAdditionalLowerSpace = 0;
+
+ IDocumentSettingAccess const& rIDSA(m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess());
+ if (rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
+ {
+ const SwFrame* pFrame = &m_rThis;
+ if ( pFrame->IsSctFrame() )
+ {
+ const SwSectionFrame* pSectFrame = static_cast<const SwSectionFrame*>(pFrame);
+ pFrame = pSectFrame->FindLastContent();
+ if ( pFrame && pFrame->IsInTab() )
+ {
+ const SwTabFrame* pTableFrame = pFrame->FindTabFrame();
+ if ( pSectFrame->IsAnLower( pTableFrame ) )
+ {
+ pFrame = pTableFrame;
+ }
+ }
+ }
+
+ std::optional<SwBorderAttrAccess> oAttrAccess;
+ if (pFrame && (!_pAttrs || pFrame != &m_rThis))
+ {
+ oAttrAccess.emplace(SwFrame::GetCache(), pFrame);
+ _pAttrs = oAttrAccess->Get();
+ }
+
+ if (_pAttrs)
+ {
+ nAdditionalLowerSpace += _pAttrs->GetULSpace().GetLower();
+
+ if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
+ {
+ nAdditionalLowerSpace += _pAttrs->CalcLineSpacing();
+ }
+ }
+ }
+
+ return nAdditionalLowerSpace;
+}
+
+/// Moves the Frame forward if it seems necessary regarding the current conditions and attributes.
+bool SwFlowFrame::CheckMoveFwd( bool& rbMakePage, bool bKeep, bool bIgnoreMyOwnKeepValue )
+{
+ const SwFrame* pNxt = m_rThis.GetIndNext();
+
+ if ( bKeep && //!bMovedBwd &&
+ ( !pNxt || ( pNxt->IsTextFrame() && static_cast<const SwTextFrame*>(pNxt)->IsEmptyMaster() ) ) &&
+ ( nullptr != (pNxt = m_rThis.FindNext()) ) && IsKeepFwdMoveAllowed(bIgnoreMyOwnKeepValue) )
+ {
+ if( pNxt->IsSctFrame() )
+ { // Don't get fooled by empty SectionFrames
+ const SwFrame* pTmp = nullptr;
+ while( pNxt && pNxt->IsSctFrame() &&
+ ( !static_cast<const SwSectionFrame*>(pNxt)->GetSection() ||
+ nullptr == ( pTmp = static_cast<const SwSectionFrame*>(pNxt)->ContainsAny() ) ) )
+ {
+ pNxt = pNxt->FindNext();
+ pTmp = nullptr;
+ }
+ if( pTmp )
+ pNxt = pTmp; // the content of the next notempty sectionfrm
+ }
+ if( pNxt && pNxt->isFrameAreaPositionValid() )
+ {
+ bool bMove = false;
+ const SwSectionFrame *pSct = m_rThis.FindSctFrame();
+ if( pSct && !pSct->isFrameAreaSizeValid() )
+ {
+ const SwSectionFrame* pNxtSct = pNxt->FindSctFrame();
+ if( pNxtSct && pSct->IsAnFollow( pNxtSct ) )
+ bMove = true;
+ }
+ else
+ bMove = true;
+ if( bMove )
+ {
+ //Keep together with the following frame
+ MoveFwd( rbMakePage, false );
+ return true;
+ }
+ }
+ }
+
+ bool bMovedFwd = false;
+
+ if ( m_rThis.GetIndPrev() )
+ {
+ if ( IsPrevObjMove() ) // Should we care about objects of the Prev?
+ {
+ bMovedFwd = true;
+ if ( !MoveFwd( rbMakePage, false ) )
+ rbMakePage = false;
+ }
+ else
+ {
+ if ( IsPageBreak( false ) )
+ {
+ while ( MoveFwd( rbMakePage, true ) )
+ /* do nothing */;
+ rbMakePage = false;
+ bMovedFwd = true;
+ }
+ else if ( IsColBreak ( false ) )
+ {
+ const SwPageFrame *pPage = m_rThis.FindPageFrame();
+ SwFrame *pCol = m_rThis.FindColFrame();
+ do
+ { MoveFwd( rbMakePage, false );
+ SwFrame *pTmp = m_rThis.FindColFrame();
+ if( pTmp != pCol )
+ {
+ bMovedFwd = true;
+ pCol = pTmp;
+ }
+ else
+ break;
+ } while ( IsColBreak( false ) );
+ if ( pPage != m_rThis.FindPageFrame() )
+ rbMakePage = false;
+ }
+ }
+ }
+ return bMovedFwd;
+}
+
+bool SwFlowFrame::ForbiddenForFootnoteCntFwd() const
+{
+ return m_rThis.IsTabFrame() || m_rThis.IsInTab();
+}
+
+/// Return value guarantees that a new page was not created,
+/// although false does not NECESSARILY indicate that a new page was created.
+/// Either false or true(MoveFootnoteCntFwd) can be returned if no changes were made
+bool SwFlowFrame::MoveFwd( bool bMakePage, bool bPageBreak, bool bMoveAlways )
+{
+//!!!!MoveFootnoteCntFwd might need to be updated as well.
+ SwFootnoteBossFrame *pOldBoss = m_rThis.FindFootnoteBossFrame();
+ if (m_rThis.IsInFootnote())
+ {
+ assert(!ForbiddenForFootnoteCntFwd()); // prevented by IsMoveable()
+ if (!m_rThis.IsContentFrame() || !pOldBoss)
+ {
+ SAL_WARN("sw.core", "Tables in footnotes are not truly supported");
+ return false;
+ }
+ return static_cast<SwContentFrame&>(m_rThis).MoveFootnoteCntFwd( bMakePage, pOldBoss );
+ }
+
+ if( !IsFwdMoveAllowed() && !bMoveAlways )
+ {
+ bool bNoFwd = true;
+ if( m_rThis.IsInSct() )
+ {
+ SwFootnoteBossFrame* pBoss = m_rThis.FindFootnoteBossFrame();
+ bNoFwd = !pBoss->IsInSct() || ( !pBoss->Lower()->GetNext() &&
+ !pBoss->GetPrev() );
+ }
+
+ // Allow the MoveFwd even if we do not have an IndPrev in these cases:
+ if ( m_rThis.IsInTab() &&
+ ( !m_rThis.IsTabFrame() ||
+ m_rThis.GetUpper()->IsInTab() ) &&
+ nullptr != m_rThis.GetNextCellLeaf() )
+ {
+ bNoFwd = false;
+ }
+
+ if( bNoFwd )
+ {
+ // It's allowed to move PageBreaks if the Frame isn't the first
+ // one on the page.
+ if ( !bPageBreak )
+ return false;
+
+ const SwFrame *pCol = m_rThis.FindColFrame();
+ if ( !pCol || !pCol->GetPrev() )
+ return false;
+ }
+ }
+
+ std::optional<SwFrameDeleteGuard> oDeleteGuard;
+ if (bMakePage)
+ oDeleteGuard.emplace(pOldBoss);
+
+ bool bSamePage = true;
+ SwLayoutFrame *pNewUpper =
+ m_rThis.GetLeaf( bMakePage ? MAKEPAGE_INSERT : MAKEPAGE_NONE, true );
+
+ if ( pNewUpper )
+ {
+ PROTOCOL_ENTER( &m_rThis, PROT::MoveFwd, DbgAction::NONE, nullptr );
+ SwPageFrame *pOldPage = pOldBoss->FindPageFrame();
+ // We move ourself and all the direct successors before the
+ // first ContentFrame below the new Upper.
+
+ // If our NewUpper lies in a SectionFrame, we need to make sure
+ // that it won't destroy itself in Calc.
+ SwSectionFrame* pSect = pNewUpper->FindSctFrame();
+ if( pSect )
+ {
+ // If we only switch column within our SectionFrame, we better don't
+ // call Calc, as this would format the SectionFrame, which in turn would
+ // call us again, etc.
+ if( pSect != m_rThis.FindSctFrame() )
+ {
+ bool bUnlock = !pSect->IsColLocked();
+ pSect->ColLock();
+ pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+ if( bUnlock )
+ pSect->ColUnlock();
+ }
+ }
+ // Do not calculate split cell frames.
+ else if ( !pNewUpper->IsCellFrame() || pNewUpper->Lower() )
+ pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+
+ SwFootnoteBossFrame *pNewBoss = pNewUpper->FindFootnoteBossFrame();
+ bool bBossChg = pNewBoss != pOldBoss;
+ pNewBoss = pNewBoss->FindFootnoteBossFrame( true );
+ pOldBoss = pOldBoss->FindFootnoteBossFrame( true );
+ SwPageFrame* pNewPage = pOldPage;
+
+ oDeleteGuard.reset();
+
+ // First, we move the footnotes.
+ bool bFootnoteMoved = false;
+
+ // i#26831
+ // If pSect has just been created, the printing area of pSect has
+ // been calculated based on the first content of its follow.
+ // In this case we prefer to call a SimpleFormat for this new
+ // section after we inserted the contents. Otherwise the section
+ // frame will invalidate its lowers, if its printing area changes
+ // in SwSectionFrame::Format, which can cause loops.
+ const bool bForceSimpleFormat = pSect && pSect->HasFollow() &&
+ !pSect->ContainsAny();
+
+ if ( pNewBoss != pOldBoss )
+ {
+ pNewPage = pNewBoss->FindPageFrame();
+ bSamePage = pNewPage == pOldPage;
+ // Set deadline, so the footnotes don't think up
+ // silly things...
+ SwRectFnSet aRectFnSet(pOldBoss);
+ SwSaveFootnoteHeight aHeight( pOldBoss,
+ aRectFnSet.GetBottom(pOldBoss->getFrameArea()) );
+ SwContentFrame* pStart = m_rThis.IsContentFrame() ?
+ static_cast<SwContentFrame*>(&m_rThis) : static_cast<SwLayoutFrame&>(m_rThis).ContainsContent();
+ OSL_ENSURE( pStart || ( m_rThis.IsTabFrame() && !static_cast<SwTabFrame&>(m_rThis).Lower() ),
+ "MoveFwd: Missing Content" );
+ SwLayoutFrame* pBody = pStart ? ( pStart->IsTextFrame() ?
+ const_cast<SwBodyFrame *>(static_cast<SwTextFrame*>(pStart)->FindBodyFrame()) : nullptr ) : nullptr;
+ if( pBody )
+ bFootnoteMoved = pBody->MoveLowerFootnotes( pStart, pOldBoss, pNewBoss,
+ false);
+ }
+ // It's possible when dealing with SectionFrames that we have been moved
+ // by pNewUpper->Calc(), for instance into the pNewUpper.
+ // MoveSubTree or PasteTree respectively is not prepared to handle such a
+ // situation.
+ if( pNewUpper != m_rThis.GetUpper() )
+ {
+ // i#27145
+ SwSectionFrame* pOldSct = nullptr;
+ if ( m_rThis.GetUpper()->IsSctFrame() )
+ {
+ pOldSct = static_cast<SwSectionFrame*>(m_rThis.GetUpper());
+ }
+
+ MoveSubTree( pNewUpper, pNewUpper->Lower() );
+
+ // i#27145
+ if ( pOldSct && pOldSct->GetSection() )
+ {
+ // Prevent loops by setting the new height at
+ // the section frame if footnotes have been moved.
+ // Otherwise the call of SwLayNotify::~SwLayNotify() for
+ // the (invalid) section frame will invalidate the first
+ // lower of its follow, because it grows due to the removed
+ // footnotes.
+ // Note: If pOldSct has become empty during MoveSubTree, it
+ // has already been scheduled for removal. No SimpleFormat
+ // for these.
+ pOldSct->SimpleFormat();
+ }
+
+ // i#26831
+ if ( bForceSimpleFormat )
+ {
+ pSect->SimpleFormat();
+ }
+
+ if ( bFootnoteMoved && !bSamePage )
+ {
+ pOldPage->UpdateFootnoteNum();
+ pNewPage->UpdateFootnoteNum();
+ }
+
+ if( bBossChg )
+ {
+ m_rThis.Prepare( PrepareHint::BossChanged, nullptr, false );
+ if( !bSamePage )
+ {
+ SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+ if ( pSh && !pSh->Imp()->IsUpdateExpFields() )
+ pSh->GetDoc()->getIDocumentFieldsAccess().SetNewFieldLst(true); // Will be done by CalcLayout() later on!
+
+ pNewPage->InvalidateSpelling();
+ pNewPage->InvalidateSmartTags();
+ pNewPage->InvalidateAutoCompleteWords();
+ pNewPage->InvalidateWordCount();
+ }
+ }
+ }
+ // No <CheckPageDesc(..)> in online layout
+ const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+
+ if ( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) )
+ {
+ // i#106452
+ // check page description not only in situation with sections.
+ if ( !bSamePage &&
+ ( m_rThis.GetPageDescItem().GetPageDesc() ||
+ pOldPage->GetPageDesc()->GetFollow() != pNewPage->GetPageDesc() ) )
+ {
+ SwFrame::CheckPageDescs( pNewPage, false );
+ }
+ }
+ }
+ return bSamePage;
+}
+
+/** Return value tells whether any changes have been made.
+ * If true, the frame has moved backwards to an earlier column/section/frame/page etc.
+ *
+ * @note This should be called by derived classes.
+ * @note The actual moving must be implemented in the subclasses via Cut()/Paste().
+ */
+bool SwFlowFrame::MoveBwd( bool &rbReformat )
+{
+ SwFlowFrame::SetMoveBwdJump( false );
+
+ SwFootnoteFrame* pFootnote = m_rThis.FindFootnoteFrame();
+ if ( pFootnote && pFootnote->IsBackMoveLocked() )
+ return false;
+
+ // Text frames, which are directly inside
+ // tables aren't allowed to move backward.
+ if ( m_rThis.IsTextFrame() && m_rThis.IsInTab() )
+ {
+ const SwLayoutFrame* pUpperFrame = m_rThis.GetUpper();
+ while ( pUpperFrame )
+ {
+ if ( pUpperFrame->IsTabFrame() || pUpperFrame->IsRowFrame() )
+ {
+ return false;
+ }
+ // If the text frame is a follow-section-in-table, that can move
+ // backward as well.
+ bool bIsFollowSection = pUpperFrame->IsSctFrame() && static_cast<const SwSectionFrame*>(pUpperFrame)->GetPrecede();
+
+ // If the text frame is a follow-in-table, that can move
+ // backward as well.
+ bool bIsFollow = const_cast<SwLayoutFrame*>(pUpperFrame)->GetPrevCellLeaf();
+
+ if ( ( pUpperFrame->IsColumnFrame() && pUpperFrame->IsInSct() ) || bIsFollowSection || bIsFollow )
+ {
+ break;
+ }
+ pUpperFrame = pUpperFrame->GetUpper();
+ }
+ }
+
+ SwFootnoteBossFrame * pOldBoss = m_rThis.FindFootnoteBossFrame();
+ if (!pOldBoss)
+ return false;
+
+ SwPageFrame * const pOldPage = pOldBoss->FindPageFrame();
+ SwLayoutFrame *pNewUpper = nullptr;
+ bool bCheckPageDescs = false;
+ bool bCheckPageDescOfNextPage = false;
+
+ if ( pFootnote )
+ {
+ // If the footnote already sits on the same page/column as the reference,
+ // we can't flow back. The breaks don't need to be checked for footnotes.
+
+ // i#37084 FindLastContent does not necessarily
+ // have to have a result != 0
+ SwFrame* pRef = nullptr;
+ const bool bEndnote = pFootnote->GetAttr()->GetFootnote().IsEndNote();
+ const IDocumentSettingAccess& rSettings
+ = pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
+ if( bEndnote && pFootnote->IsInSct() )
+ {
+ SwSectionFrame* pSect = pFootnote->FindSctFrame();
+ if( pSect->IsEndnAtEnd() )
+ // Endnotes at the end of the section.
+ pRef = pSect->FindLastContent( SwFindMode::LastCnt );
+ }
+ else if (bEndnote && rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
+ {
+ // Endnotes at the end of the document.
+ SwPageFrame* pPage = m_rThis.getRootFrame()->GetLastPage();
+ pRef = pPage->FindLastBodyContent();
+ }
+ if( !pRef )
+ // Endnotes on a separate page.
+ pRef = pFootnote->GetRef();
+
+ OSL_ENSURE( pRef, "MoveBwd: Endnote for an empty section?" );
+
+ if( !bEndnote )
+ pOldBoss = pOldBoss->FindFootnoteBossFrame( true );
+ SwFootnoteBossFrame *pRefBoss = pRef->FindFootnoteBossFrame( !bEndnote );
+ if ( pOldBoss != pRefBoss &&
+
+ ( !bEndnote ||
+ pRefBoss->IsBefore( pOldBoss ) )
+ )
+ pNewUpper = m_rThis.GetLeaf( MAKEPAGE_FTN, false );
+ }
+ else if ( IsPageBreak( true ) ) // Do we have to respect a PageBreak?
+ {
+ // If the previous page doesn't have a Frame in the body,
+ // flowing back makes sense despite the PageBreak (otherwise,
+ // we'd get an empty page).
+ // Of course we need to overlook empty pages!
+ const SwFrame *pFlow = &m_rThis;
+ do
+ {
+ pFlow = pFlow->FindPrev();
+ } while ( pFlow &&
+ ( pFlow->FindPageFrame() == pOldPage ||
+ !pFlow->IsInDocBody() ) );
+ if ( pFlow )
+ {
+ tools::Long nDiff = pOldPage->GetPhyPageNum() - pFlow->GetPhyPageNum();
+ if ( nDiff > 1 )
+ {
+ if ( static_cast<SwPageFrame*>(pOldPage->GetPrev())->IsEmptyPage() )
+ nDiff -= 1;
+ if ( nDiff > 1 )
+ {
+ pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
+ // i#53139
+ // Now <pNewUpper> is a previous layout frame, which contains
+ // content. But the new upper layout frame has to be the next one.
+ // Thus, hack for issue i14206 no longer needed, but fix for issue 114442
+ // Correct fix for i53139
+ // Check for wrong page description before using next new upper.
+ // i#66051 - further correction of fix for i53139
+ // Check for correct type of new next upper layout frame
+ // Another correction of fix for i53139
+ // Assumption, that in all cases <pNewUpper> is a previous
+ // layout frame, which contains content, is wrong.
+ // Another correction of fix for i53139
+ // Beside type check, check also, if proposed new next upper
+ // frame is inside the same frame types.
+ // i#73194 - and yet another correction
+ // of fix for i53139:
+ // Assure that the new next upper layout frame doesn't
+ // equal the current one.
+ // E.g.: content is on page 3, on page 2 is only a 'ghost'
+ // section and on page 1 is normal content. Method <FindPrev(..)>
+ // will find the last content of page 1, but <GetLeaf(..)>
+ // returns new upper on page 2.
+ if (pNewUpper && pNewUpper->Lower())
+ {
+ SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NONE, true );
+ if ( pNewNextUpper &&
+ pNewNextUpper != m_rThis.GetUpper() &&
+ pNewNextUpper->GetType() == pNewUpper->GetType() &&
+ pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
+ pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
+ pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
+ pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
+ !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
+ {
+ pNewUpper = pNewNextUpper;
+ bCheckPageDescOfNextPage = true;
+ }
+ }
+
+ bCheckPageDescs = true;
+ }
+ }
+ }
+ }
+ else if ( IsColBreak( true ) )
+ {
+ // If the previous column doesn't contain a ContentFrame, flowing back
+ // makes sense despite the ColumnBreak, as otherwise we'd get
+ // an empty column.
+ if( m_rThis.IsInSct() )
+ {
+ pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
+ if( pNewUpper && !SwFlowFrame::IsMoveBwdJump() &&
+ ( pNewUpper->ContainsContent() ||
+ ( ( !pNewUpper->IsColBodyFrame() ||
+ !pNewUpper->GetUpper()->GetPrev() ) &&
+ !pNewUpper->FindSctFrame()->GetPrev() ) ) )
+ {
+ pNewUpper = nullptr;
+ }
+ // i#53139
+ // i#69409 - check <pNewUpper>
+ // i#71065 - check <SwFlowFrame::IsMoveBwdJump()>
+ else if ( pNewUpper && !SwFlowFrame::IsMoveBwdJump() )
+ {
+ // Now <pNewUpper> is a previous layout frame, which
+ // contains content. But the new upper layout frame
+ // has to be the next one.
+ // Correct fix for i53139
+ // Check for wrong page description before using next new upper.
+ // i#66051 - further correction of fix for i53139
+ // Check for correct type of new next upper layout frame
+ // Another correction of fix for i53139
+ // Beside type check, check also, if proposed new next upper
+ // frame is inside the same frame types.
+ SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NOSECTION, true );
+ if ( pNewNextUpper &&
+ pNewNextUpper->GetType() == pNewUpper->GetType() &&
+ pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
+ pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
+ pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
+ pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
+ !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
+ {
+ pNewUpper = pNewNextUpper;
+ }
+ }
+ }
+ else
+ {
+ const SwFrame *pCol = m_rThis.FindColFrame();
+ assert(pCol);
+ bool bGoOn = true;
+ bool bJump = false;
+ do
+ {
+ if ( pCol->GetPrev() )
+ pCol = pCol->GetPrev();
+ else
+ {
+ bGoOn = false;
+ pCol = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
+ }
+ if ( pCol )
+ {
+ // ColumnFrames now with BodyFrame
+ SwLayoutFrame* pColBody = pCol->IsColumnFrame() ?
+ const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol)->Lower())) :
+ const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol));
+ if ( pColBody->ContainsContent() )
+ {
+ bGoOn = false; // We have content here! we accept this
+ // only if GetLeaf() has set the MoveBwdJump.
+ if( SwFlowFrame::IsMoveBwdJump() )
+ {
+ pNewUpper = pColBody;
+ // i#53139
+ // Now <pNewUpper> is a previous layout frame, which
+ // contains content. But the new upper layout frame
+ // has to be the next one.
+ // Correct fix for i53139
+ // Check for wrong page description before using next new upper.
+ // i#66051 - further correction of fix for i53139
+ // Check for correct type of new next upper layout frame
+ // Another correction of fix for i53139
+ // Beside type check, check also, if proposed new next upper
+ // frame is inside the same frame types.
+ // i#71065
+ // Check that the proposed new next upper layout
+ // frame isn't the current one.
+ SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NONE, true );
+ if ( pNewNextUpper &&
+ pNewNextUpper != m_rThis.GetUpper() &&
+ pNewNextUpper->GetType() == pNewUpper->GetType() &&
+ pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
+ pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
+ pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
+ pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
+ !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
+ {
+ pNewUpper = pNewNextUpper;
+ }
+ }
+ }
+ else
+ {
+ if( pNewUpper ) // We already had an empty column, in other
+ bJump = true; // words we skipped one.
+ pNewUpper = pColBody; // this empty column could be considered,
+ // but we continue searching nevertheless.
+ }
+ }
+ } while( bGoOn );
+ if( bJump )
+ SwFlowFrame::SetMoveBwdJump( true );
+ }
+ }
+ else // No breaks - we can flow back.
+ pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
+
+ // i#27801 - no move backward of 'master' text frame,
+ // if - due to its object positioning - it isn't allowed to be on the new page frame
+ // i#44049 - add another condition for not moving backward
+ // If one of its objects has restarted the layout process, moving backward
+ // isn't sensible either.
+ // i#47697 - refine condition made for issue i44049
+ // - allow move backward as long as the anchored object is only temporarily
+ // positions considering its wrapping style.
+ if ( pNewUpper &&
+ m_rThis.IsTextFrame() && !IsFollow() )
+ {
+ sal_uInt32 nToPageNum( 0 );
+ const bool bMoveFwdByObjPos = SwLayouter::FrameMovedFwdByObjPos(
+ *(pOldPage->GetFormat()->GetDoc()),
+ static_cast<SwTextFrame&>(m_rThis),
+ nToPageNum );
+ if ( bMoveFwdByObjPos &&
+ pNewUpper->FindPageFrame()->GetPhyPageNum() < nToPageNum )
+ {
+ pNewUpper = nullptr;
+ }
+ // i#44049 - check, if one of its anchored objects
+ // has restarted the layout process.
+ else if ( m_rThis.GetDrawObjs() )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *m_rThis.GetDrawObjs())
+ {
+ // i#47697 - refine condition - see above
+ if ( pAnchoredObj->RestartLayoutProcess() &&
+ !pAnchoredObj->IsTmpConsiderWrapInfluence() )
+ {
+ pNewUpper = nullptr;
+ break;
+ }
+ }
+ }
+ }
+
+ // With Follows, it's only allowed to flow back if there's no neighbor
+ // in the new environment (because that would be the Master).
+ // (6677) If however we skipped empty pages, we still have to move.
+ if ( pNewUpper && IsFollow() && pNewUpper->Lower() )
+ {
+ // i#79774
+ // neglect empty sections in proposed new upper frame
+ bool bProposedNewUpperContainsOnlyEmptySections( true );
+ {
+ const SwFrame* pLower( pNewUpper->Lower() );
+ while ( pLower )
+ {
+ if ( pLower->IsSctFrame() &&
+ !dynamic_cast<const SwSectionFrame&>(*pLower).GetSection() )
+ {
+ pLower = pLower->GetNext();
+ continue;
+ }
+ else
+ {
+ bProposedNewUpperContainsOnlyEmptySections = false;
+ break;
+ }
+ }
+ }
+ if ( !bProposedNewUpperContainsOnlyEmptySections )
+ {
+ if ( SwFlowFrame::IsMoveBwdJump() )
+ {
+ // Don't move after the Master, but into the next empty page.
+ SwFrame *pFrame = pNewUpper->Lower();
+ while ( pFrame->GetNext() )
+ pFrame = pFrame->GetNext();
+ pNewUpper = pFrame->GetLeaf( MAKEPAGE_INSERT, true );
+ if( pNewUpper == m_rThis.GetUpper() ) // Did we end up in the same place?
+ pNewUpper = nullptr; // If so, moving is not needed.
+ }
+ else
+ pNewUpper = nullptr;
+ }
+ }
+ if ( pNewUpper && !ShouldBwdMoved( pNewUpper, rbReformat ) )
+ {
+ if( !pNewUpper->Lower() )
+ {
+ if( pNewUpper->IsFootnoteContFrame() )
+ {
+ pNewUpper->Cut();
+ SwFrame::DestroyFrame(pNewUpper);
+ }
+ else
+ {
+ SwSectionFrame* pSectFrame = pNewUpper->FindSctFrame();
+
+ if ( pSectFrame && !pSectFrame->IsColLocked() &&
+ !pSectFrame->ContainsContent() && !pSectFrame->ContainsAny( true ) )
+ {
+ pSectFrame->DelEmpty( true );
+ SwFrame::DestroyFrame(pSectFrame);
+ m_rThis.setFrameAreaPositionValid(true);
+ }
+ }
+ }
+ pNewUpper = nullptr;
+ }
+
+ // i#21478 - don't move backward, if flow frame wants to
+ // keep with next frame and next frame is locked.
+ // i#38232 - If next frame is a table, do *not* check,
+ // if it's locked.
+ if ( pNewUpper && !IsFollow() &&
+ m_rThis.GetAttrSet()->GetKeep().GetValue() && m_rThis.GetIndNext() )
+ {
+ SwFrame* pIndNext = m_rThis.GetIndNext();
+ // i#38232
+ if ( !pIndNext->IsTabFrame() )
+ {
+ // get first content of section, while empty sections are skipped
+ while ( pIndNext && pIndNext->IsSctFrame() )
+ {
+ if( static_cast<SwSectionFrame*>(pIndNext)->GetSection() )
+ {
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pIndNext)->ContainsAny();
+ if ( pTmp )
+ {
+ pIndNext = pTmp;
+ break;
+ }
+ }
+ pIndNext = pIndNext->GetIndNext();
+ }
+ OSL_ENSURE( !pIndNext || dynamic_cast<const SwTextFrame*>( pIndNext) != nullptr,
+ "<SwFlowFrame::MovedBwd(..)> - incorrect next found." );
+ if ( pIndNext && pIndNext->IsFlowFrame() &&
+ SwFlowFrame::CastFlowFrame(pIndNext)->IsJoinLocked() )
+ {
+ pNewUpper = nullptr;
+ }
+ }
+ }
+
+ // i#65250
+ // layout loop control for flowing content again and again moving
+ // backward under the same layout condition.
+ if ( pNewUpper && !IsFollow() &&
+ pNewUpper != m_rThis.GetUpper() &&
+ SwLayouter::MoveBwdSuppressed( *(pOldPage->GetFormat()->GetDoc()),
+ *this, *pNewUpper ) )
+ {
+ SwLayoutFrame* pNextNewUpper = pNewUpper->GetLeaf(
+ ( !m_rThis.IsSctFrame() && m_rThis.IsInSct() )
+ ? MAKEPAGE_NOSECTION
+ : MAKEPAGE_NONE,
+ true );
+ // i#73194 - make code robust
+ OSL_ENSURE( pNextNewUpper, "<SwFlowFrame::MoveBwd(..)> - missing next new upper" );
+ if ( pNextNewUpper &&
+ ( pNextNewUpper == m_rThis.GetUpper() ||
+ pNextNewUpper->GetType() != m_rThis.GetUpper()->GetType() ) )
+ {
+ // tdf#107398 do not leave empty footnote container around
+ if (!pNewUpper->Lower() && pNewUpper->IsFootnoteContFrame())
+ {
+ pNewUpper->Cut();
+ SwFrame::DestroyFrame(pNewUpper);
+ }
+ pNewUpper = nullptr;
+ OSL_FAIL( "<SwFlowFrame::MoveBwd(..)> - layout loop control for layout action <Move Backward> applied!" );
+ }
+ }
+
+ OSL_ENSURE( pNewUpper != m_rThis.GetUpper(),
+ "<SwFlowFrame::MoveBwd(..)> - moving backward to the current upper frame!?" );
+ if ( pNewUpper )
+ {
+ PROTOCOL_ENTER( &m_rThis, PROT::MoveBack, DbgAction::NONE, nullptr );
+ if ( pNewUpper->IsFootnoteContFrame() )
+ {
+ // I may have gotten a Container
+ SwFootnoteFrame *pNew = SwFootnoteContFrame::PrependChained(&m_rThis, false);
+ pNew->Paste( pNewUpper );
+ pNewUpper = pNew;
+ }
+ if( pNewUpper->IsFootnoteFrame() && m_rThis.IsInSct() )
+ {
+ SwSectionFrame* pSct = m_rThis.FindSctFrame();
+ // If we're in a section of a footnote, we may need to create
+ // a SwSectionFrame in the new upper
+ if( pSct->IsInFootnote() )
+ {
+ SwFrame* pTmp = pNewUpper->Lower();
+ if( pTmp )
+ {
+ while( pTmp->GetNext() )
+ pTmp = pTmp->GetNext();
+ if( !pTmp->IsSctFrame() ||
+ static_cast<SwSectionFrame*>(pTmp)->GetFollow() != pSct )
+ pTmp = nullptr;
+ }
+ if( pTmp )
+ pNewUpper = static_cast<SwSectionFrame*>(pTmp);
+ else
+ {
+ pSct = new SwSectionFrame( *pSct, true );
+ pSct->Paste( pNewUpper );
+ pSct->Init();
+ pNewUpper = pSct;
+ pSct->SimpleFormat();
+ }
+ }
+ }
+ bool bUnlock = false;
+ bool bFollow = false;
+ // Lock section. Otherwise, it could get destroyed if the only Content
+ // moves e.g. from the second into the first column.
+ SwSectionFrame* pSect = pNewUpper->FindSctFrame();
+ if( pSect )
+ {
+ bUnlock = !pSect->IsColLocked();
+ pSect->ColLock();
+ bFollow = pSect->HasFollow();
+ }
+
+ {
+ auto const pOld = m_rThis.GetUpper();
+ ::std::optional<SwFrameDeleteGuard> g;
+ if (m_rThis.GetUpper()->IsCellFrame())
+ {
+ // note: IsFollowFlowRow() is never set for new-style tables
+ SwTabFrame const*const pTabFrame(m_rThis.FindTabFrame());
+ if ( pTabFrame->IsFollow()
+ && static_cast<SwTabFrame const*>(pTabFrame->GetPrecede())->HasFollowFlowLine()
+ && pTabFrame->GetFirstNonHeadlineRow() == m_rThis.GetUpper()->GetUpper())
+ {
+ // lock follow-flow-row (similar to sections above)
+ g.emplace(m_rThis.GetUpper()->GetUpper());
+ assert(m_rThis.GetUpper()->GetUpper()->IsDeleteForbidden());
+ }
+ }
+ pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+ SAL_WARN_IF(pOld != m_rThis.GetUpper(), "sw.core",
+ "MoveBwd(): pNewUpper->Calc() moved this frame?");
+ }
+
+ m_rThis.Cut();
+
+ // optimization: format section, if its size is invalidated and if it's
+ // the new parent of moved backward frame.
+ bool bFormatSect( false );
+ if( bUnlock )
+ {
+ pSect->ColUnlock();
+ if( pSect->HasFollow() != bFollow )
+ {
+ pSect->InvalidateSize();
+ // - optimization
+ if ( pSect == pNewUpper )
+ bFormatSect = true;
+ }
+ }
+
+ m_rThis.Paste( pNewUpper );
+ // - optimization
+ if ( bFormatSect )
+ pSect->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+
+ SwPageFrame *pNewPage = m_rThis.FindPageFrame();
+ if( pNewPage != pOldPage )
+ {
+ m_rThis.Prepare( PrepareHint::BossChanged, static_cast<const void*>(pOldPage), false );
+ SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+ if ( pSh && !pSh->Imp()->IsUpdateExpFields() )
+ pSh->GetDoc()->getIDocumentFieldsAccess().SetNewFieldLst(true); // Will be done by CalcLayout() later on
+
+ pNewPage->InvalidateSpelling();
+ pNewPage->InvalidateSmartTags();
+ pNewPage->InvalidateAutoCompleteWords();
+ pNewPage->InvalidateWordCount();
+
+ // No <CheckPageDesc(..)> in online layout
+ if ( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) )
+ {
+ if ( bCheckPageDescs && pNewPage->GetNext() )
+ {
+ SwPageFrame* pStartPage = bCheckPageDescOfNextPage ?
+ pNewPage :
+ static_cast<SwPageFrame*>(pNewPage->GetNext());
+ SwFrame::CheckPageDescs( pStartPage, false);
+ }
+ else if (m_rThis.GetPageDescItem().GetPageDesc())
+ {
+ // First page could get empty for example by disabling
+ // a section
+ SwFrame::CheckPageDescs( pNewPage, false);
+ }
+ }
+ }
+ }
+ return pNewUpper != nullptr;
+}
+
+SwFlowFrame *SwFlowFrame::CastFlowFrame( SwFrame *pFrame )
+{
+ if ( pFrame->IsContentFrame() )
+ return static_cast<SwContentFrame*>(pFrame);
+ if ( pFrame->IsTabFrame() )
+ return static_cast<SwTabFrame*>(pFrame);
+ if ( pFrame->IsSctFrame() )
+ return static_cast<SwSectionFrame*>(pFrame);
+ return nullptr;
+}
+
+const SwFlowFrame *SwFlowFrame::CastFlowFrame( const SwFrame *pFrame )
+{
+ if ( pFrame->IsContentFrame() )
+ return static_cast<const SwContentFrame*>(pFrame);
+ if ( pFrame->IsTabFrame() )
+ return static_cast<const SwTabFrame*>(pFrame);
+ if ( pFrame->IsSctFrame() )
+ return static_cast<const SwSectionFrame*>(pFrame);
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx
new file mode 100644
index 000000000..99f976eab
--- /dev/null
+++ b/sw/source/core/layout/fly.cxx
@@ -0,0 +1,2997 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <svl/itemiter.hxx>
+#include <vcl/imap.hxx>
+#include <tools/helpers.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <fmtfsize.hxx>
+#include <fmtclds.hxx>
+#include <fmtcntnt.hxx>
+#include <fmturl.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtornt.hxx>
+#include <fmtcnct.hxx>
+#include <ndgrf.hxx>
+#include <tolayoutanchoredobjectposition.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <sortedobjs.hxx>
+#include <objectformatter.hxx>
+#include <ndole.hxx>
+#include <swtable.hxx>
+#include <svx/svdoashp.hxx>
+#include <layouter.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <dcontact.hxx>
+#include <dflyobj.hxx>
+#include <dview.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <hints.hxx>
+#include <tabfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <flyfrms.hxx>
+#include <sectfrm.hxx>
+#include <vcl/svapp.hxx>
+#include <calbck.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <textboxhelper.hxx>
+#include <txtfly.hxx>
+#include <ndindex.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <osl/diagnose.h>
+
+#include <wrtsh.hxx>
+#include <view.hxx>
+#include <edtwin.hxx>
+#include <bodyfrm.hxx>
+#include <FrameControlsManager.hxx>
+#include <ndtxt.hxx>
+
+using namespace ::com::sun::star;
+
+static SwTwips lcl_CalcAutoWidth( const SwLayoutFrame& rFrame );
+
+SwFlyFrame::SwFlyFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch ) :
+ SwLayoutFrame( pFormat, pSib ),
+ // #i26791#
+ m_pPrevLink( nullptr ),
+ m_pNextLink( nullptr ),
+ m_bInCnt( false ),
+ m_bAtCnt( false ),
+ m_bLayout( false ),
+ m_bAutoPosition( false ),
+ m_bDeleted( false ),
+ m_nAuthor( std::string::npos ),
+ m_bValidContentPos( false )
+{
+ mnFrameType = SwFrameType::Fly;
+
+ m_bInvalid = m_bNotifyBack = true;
+ m_bLocked = m_bMinHeight =
+ m_bHeightClipped = m_bWidthClipped = m_bFormatHeightOnly = false;
+
+ // Size setting: Fixed size is always the width
+ const SwFormatFrameSize &rFrameSize = pFormat->GetFrameSize();
+ const SvxFrameDirection nDir = pFormat->GetFormatAttr( RES_FRAMEDIR ).GetValue();
+ if( SvxFrameDirection::Environment == nDir )
+ {
+ mbDerivedVert = true;
+ mbDerivedR2L = true;
+ }
+ else
+ {
+ mbInvalidVert = false;
+ mbDerivedVert = false;
+ mbDerivedR2L = false;
+ if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir )
+ {
+ mbVertLR = false;
+ mbVertical = false;
+ }
+ else
+ {
+ const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ mbVertLR = false;
+ mbVertical = false;
+ }
+ else
+ {
+ mbVertical = true;
+
+ if ( SvxFrameDirection::Vertical_LR_TB == nDir )
+ mbVertLR = true;
+ else if (nDir == SvxFrameDirection::Vertical_LR_BT)
+ {
+ mbVertLR = true;
+ mbVertLRBT = true;
+ }
+ else
+ mbVertLR = false;
+ }
+ }
+
+ mbInvalidR2L = false;
+ if( SvxFrameDirection::Horizontal_RL_TB == nDir )
+ mbRightToLeft = true;
+ else
+ mbRightToLeft = false;
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( rFrameSize.GetWidth() );
+ aFrm.Height( rFrameSize.GetHeightSizeType() == SwFrameSize::Variable ? MINFLY : rFrameSize.GetHeight() );
+ }
+
+ // Fixed or variable Height?
+ if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum )
+ m_bMinHeight = true;
+ else if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Fixed )
+ mbFixSize = true;
+
+ // insert columns, if necessary
+ InsertColumns();
+
+ // First the Init, then the Content:
+ // This is due to the fact that the Content may have Objects/Frames,
+ // which are then registered
+ InitDrawObj(*pAnch);
+
+ Chain( pAnch );
+
+ InsertCnt();
+
+ // Put it somewhere outside so that out document is not formatted unnecessarily often
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(FAR_AWAY);
+ aFrm.Pos().setY(FAR_AWAY);
+}
+
+void SwFlyFrame::Chain( SwFrame* _pAnch )
+{
+ // Connect to chain neighbours.
+ // No problem, if a neighbor doesn't exist - the construction of the
+ // neighbor will make the connection
+ const SwFormatChain& rChain = GetFormat()->GetChain();
+ if ( !(rChain.GetPrev() || rChain.GetNext()) )
+ return;
+
+ if ( rChain.GetNext() )
+ {
+ SwFlyFrame* pFollow = FindChainNeighbour( *rChain.GetNext(), _pAnch );
+ if ( pFollow )
+ {
+ OSL_ENSURE( !pFollow->GetPrevLink(), "wrong chain detected" );
+ if ( !pFollow->GetPrevLink() )
+ SwFlyFrame::ChainFrames( this, pFollow );
+ }
+ }
+ if ( rChain.GetPrev() )
+ {
+ SwFlyFrame *pMaster = FindChainNeighbour( *rChain.GetPrev(), _pAnch );
+ if ( pMaster )
+ {
+ OSL_ENSURE( !pMaster->GetNextLink(), "wrong chain detected" );
+ if ( !pMaster->GetNextLink() )
+ SwFlyFrame::ChainFrames( pMaster, this );
+ }
+ }
+}
+
+void SwFlyFrame::InsertCnt()
+{
+ if ( GetPrevLink() )
+ return;
+
+ const SwFormatContent& rContent = GetFormat()->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), ":-( no content prepared." );
+ SwNodeOffset nIndex = rContent.GetContentIdx()->GetIndex();
+ // Lower() means SwColumnFrame; the Content then needs to be inserted into the (Column)BodyFrame
+ ::InsertCnt_( Lower() ? static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(Lower())->Lower()) : static_cast<SwLayoutFrame*>(this),
+ GetFormat()->GetDoc(), nIndex );
+
+ // NoText always have a fixed height.
+ if ( Lower() && Lower()->IsNoTextFrame() )
+ {
+ mbFixSize = true;
+ m_bMinHeight = false;
+ }
+}
+
+void SwFlyFrame::InsertColumns()
+{
+ // #i97379#
+ // Check, if column are allowed.
+ // Columns are not allowed for fly frames, which represent graphics or embedded objects.
+ const SwFormatContent& rContent = GetFormat()->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), "<SwFlyFrame::InsertColumns()> - no content prepared." );
+ SwNodeIndex aFirstContent( *(rContent.GetContentIdx()), 1 );
+ if ( aFirstContent.GetNode().IsNoTextNode() )
+ {
+ return;
+ }
+
+ const SwFormatCol &rCol = GetFormat()->GetCol();
+ if ( rCol.GetNumCols() <= 1 )
+ return;
+
+ // Start off PrtArea to be as large as Frame, so that we can put in the columns
+ // properly. It'll adjust later on.
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( getFrameArea().Width() );
+ aPrt.Height( getFrameArea().Height() );
+ }
+
+ const SwFormatCol aOld; // ChgColumns() also needs an old value passed
+ ChgColumns( aOld, rCol );
+}
+
+void SwFlyFrame::DestroyImpl()
+{
+ // Accessible objects for fly frames will be destroyed in this destructor.
+ // For frames bound as char or frames that don't have an anchor we have
+ // to do that ourselves. For any other frame the call RemoveFly at the
+ // anchor will do that.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( IsAccessibleFrame() && GetFormat() && (IsFlyInContentFrame() || !GetAnchorFrame()) )
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
+ {
+ SwViewShell *pVSh = pRootFrame->GetCurrShell();
+ if( pVSh && pVSh->Imp() )
+ {
+ // Lowers aren't disposed already, so we have to do a recursive
+ // dispose
+ pVSh->Imp()->DisposeAccessibleFrame( this, true );
+ }
+ }
+ }
+#endif
+
+ if( GetFormat() && !GetFormat()->GetDoc()->IsInDtor() )
+ {
+ ClearTmpConsiderWrapInfluence(); // remove this from SwLayouter
+
+ Unchain();
+
+ DeleteCnt();
+
+ if ( GetAnchorFrame() )
+ AnchorFrame()->RemoveFly( this );
+ }
+
+ FinitDrawObj();
+
+ SwLayoutFrame::DestroyImpl();
+
+ SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(getRootFrame()->GetCurrShell());
+ UpdateUnfloatButton(pWrtSh, false);
+}
+
+SwFlyFrame::~SwFlyFrame()
+{
+}
+
+const IDocumentDrawModelAccess& SwFlyFrame::getIDocumentDrawModelAccess()
+{
+ return GetFormat()->getIDocumentDrawModelAccess();
+}
+
+void SwFlyFrame::Unchain()
+{
+ if ( GetPrevLink() )
+ UnchainFrames( GetPrevLink(), this );
+ if ( GetNextLink() )
+ UnchainFrames( this, GetNextLink() );
+}
+
+void SwFlyFrame::DeleteCnt()
+{
+ SwFrame* pFrame = m_pLower;
+ while ( pFrame )
+ {
+ while ( pFrame->GetDrawObjs() && pFrame->GetDrawObjs()->size() )
+ {
+ SwAnchoredObject *pAnchoredObj = (*pFrame->GetDrawObjs())[0];
+ if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ SwFrame::DestroyFrame(pFlyFrame);
+ }
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( pAnchoredObj) != nullptr )
+ {
+ // consider 'virtual' drawing objects
+ SdrObject* pObj = pAnchoredObj->DrawObj();
+ if ( auto pDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj) )
+ {
+ pDrawVirtObj->RemoveFromWriterLayout();
+ pDrawVirtObj->RemoveFromDrawingPage();
+ }
+ else
+ {
+ SwDrawContact* pContact =
+ static_cast<SwDrawContact*>(::GetUserCall( pObj ));
+ if ( pContact )
+ {
+ pContact->DisconnectFromLayout();
+ }
+ }
+ }
+ }
+
+ pFrame->RemoveFromLayout();
+ SwFrame::DestroyFrame(pFrame);
+ pFrame = m_pLower;
+ }
+
+ InvalidatePage();
+}
+
+void SwFlyFrame::InitDrawObj(SwFrame const& rAnchorFrame)
+{
+ SetDrawObj(*SwFlyDrawContact::CreateNewRef(this, GetFormat(), rAnchorFrame));
+
+ // Set the right Layer
+ IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
+ SdrLayerID nHeavenId = rIDDMA.GetHeavenId();
+ SdrLayerID nHellId = rIDDMA.GetHellId();
+ GetVirtDrawObj()->SetLayer( GetFormat()->GetOpaque().GetValue()
+ ? nHeavenId
+ : nHellId );
+}
+
+static SwPosition ResolveFlyAnchor(SwFrameFormat const& rFlyFrame)
+{
+ SwFormatAnchor const& rAnch(rFlyFrame.GetAnchor());
+ if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ { // arbitrarily pick last node
+ return SwPosition(SwNodeIndex(rFlyFrame.GetDoc()->GetNodes().GetEndOfContent(), -1));
+ }
+ else
+ {
+ SwPosition const*const pPos(rAnch.GetContentAnchor());
+ assert(pPos);
+ if (SwFrameFormat const*const pParent = pPos->nNode.GetNode().GetFlyFormat())
+ {
+ return ResolveFlyAnchor(*pParent);
+ }
+ else if (pPos->nContent.GetIdxReg())
+ {
+ return *pPos;
+ }
+ else
+ {
+ return SwPosition(*pPos->nNode.GetNode().GetContentNode(), 0);
+ }
+ }
+}
+
+void SwFlyFrame::FinitDrawObj()
+{
+ if(!GetVirtDrawObj() )
+ return;
+ SwFormat* pFormat = GetFormat();
+ // Deregister from SdrPageViews if the Objects is still selected there.
+ if(!pFormat->GetDoc()->IsInDtor())
+ {
+ SwViewShell* p1St = getRootFrame()->GetCurrShell();
+ if(p1St)
+ {
+ for(SwViewShell& rCurrentShell : p1St->GetRingContainer())
+ { // At the moment the Drawing can do just do an Unmark on everything,
+ // as the Object was already removed
+ if (rCurrentShell.HasDrawView() &&
+ rCurrentShell.Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount())
+ {
+ SwFlyFrame const*const pOldSelFly = ::GetFlyFromMarked(nullptr, &rCurrentShell);
+ if (pOldSelFly == this)
+ {
+ assert(rCurrentShell.Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1);
+ if (SwFEShell *const pFEShell = dynamic_cast<SwFEShell*>(&rCurrentShell))
+ { // tdf#131679 move any cursor out of fly
+ rCurrentShell.Imp()->GetDrawView()->UnmarkAll();
+ if (pOldSelFly)
+ {
+ SwPosition const pos(ResolveFlyAnchor(*pOldSelFly->GetFormat()));
+ SwPaM const temp(pos);
+ pFEShell->SetSelection(temp);
+ // could also call SetCursor() like SwFEShell::SelectObj()
+ // does, but that would access layout a bit much...
+ }
+ }
+ else
+ {
+ rCurrentShell.Imp()->GetDrawView()->UnmarkAll();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Else calls delete of the ContactObj
+ GetVirtDrawObj()->SetUserCall(nullptr);
+
+ // Deregisters itself at the Master
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(GetVirtDrawObj());
+ SdrObject::Free(pTemp);
+}
+
+void SwFlyFrame::ChainFrames( SwFlyFrame *pMaster, SwFlyFrame *pFollow )
+{
+ OSL_ENSURE( pMaster && pFollow, "incomplete chain" );
+ OSL_ENSURE( !pMaster->GetNextLink(), "link can not be changed" );
+ OSL_ENSURE( !pFollow->GetPrevLink(), "link can not be changed" );
+
+ pMaster->m_pNextLink = pFollow;
+ pFollow->m_pPrevLink = pMaster;
+
+ if ( pMaster->ContainsContent() )
+ {
+ // To get a text flow we need to invalidate
+ SwFrame *pInva = pMaster->FindLastLower();
+ SwRectFnSet aRectFnSet(pMaster);
+ const tools::Long nBottom = aRectFnSet.GetPrtBottom(*pMaster);
+ while ( pInva )
+ {
+ if( aRectFnSet.BottomDist( pInva->getFrameArea(), nBottom ) <= 0 )
+ {
+ pInva->InvalidateSize();
+ pInva->Prepare();
+ pInva = pInva->FindPrev();
+ }
+ else
+ pInva = nullptr;
+ }
+ }
+
+ if ( pFollow->ContainsContent() )
+ {
+ // There's only the content from the Masters left; the content from the Follow
+ // does not have any Frames left (should always be exactly one empty TextNode).
+ SwFrame *pFrame = pFollow->ContainsContent();
+ OSL_ENSURE( !pFrame->IsTabFrame() && !pFrame->FindNext(), "follow in chain contains content" );
+ pFrame->Cut();
+ SwFrame::DestroyFrame(pFrame);
+ }
+
+ // invalidate accessible relation set (accessibility wrapper)
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pSh = pMaster->getRootFrame()->GetCurrShell();
+ if( pSh )
+ {
+ SwRootFrame* pLayout = pMaster->getRootFrame();
+ if( pLayout && pLayout->IsAnyShellAccessible() )
+ pSh->Imp()->InvalidateAccessibleRelationSet( pMaster, pFollow );
+ }
+#endif
+}
+
+void SwFlyFrame::UnchainFrames( SwFlyFrame *pMaster, SwFlyFrame *pFollow )
+{
+ pMaster->m_pNextLink = nullptr;
+ pFollow->m_pPrevLink = nullptr;
+
+ if ( pFollow->ContainsContent() )
+ {
+ // The Master sucks up the content of the Follow
+ SwLayoutFrame *pUpper = pMaster;
+ if ( pUpper->Lower()->IsColumnFrame() )
+ {
+ pUpper = static_cast<SwLayoutFrame*>(pUpper->GetLastLower());
+ pUpper = static_cast<SwLayoutFrame*>(pUpper->Lower()); // The (Column)BodyFrame
+ OSL_ENSURE( pUpper && pUpper->IsColBodyFrame(), "Missing ColumnBody" );
+ }
+ SwFlyFrame *pFoll = pFollow;
+ while ( pFoll )
+ {
+ SwFrame *pTmp = ::SaveContent( pFoll );
+ if ( pTmp )
+ ::RestoreContent( pTmp, pUpper, pMaster->FindLastLower() );
+ pFoll->SetCompletePaint();
+ pFoll->InvalidateSize();
+ pFoll = pFoll->GetNextLink();
+ }
+ }
+
+ // The Follow needs his own content to be served
+ const SwFormatContent &rContent = pFollow->GetFormat()->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), ":-( No content prepared." );
+ SwNodeOffset nIndex = rContent.GetContentIdx()->GetIndex();
+ // Lower() means SwColumnFrame: this one contains another SwBodyFrame
+ ::InsertCnt_( pFollow->Lower() ? const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFollow->Lower())->Lower()))
+ : static_cast<SwLayoutFrame*>(pFollow),
+ pFollow->GetFormat()->GetDoc(), ++nIndex );
+
+ // invalidate accessible relation set (accessibility wrapper)
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pSh = pMaster->getRootFrame()->GetCurrShell();
+ if( pSh )
+ {
+ SwRootFrame* pLayout = pMaster->getRootFrame();
+ if( pLayout && pLayout->IsAnyShellAccessible() )
+ pSh->Imp()->InvalidateAccessibleRelationSet( pMaster, pFollow );
+ }
+#endif
+}
+
+SwFlyFrame *SwFlyFrame::FindChainNeighbour( SwFrameFormat const &rChain, SwFrame *pAnch )
+{
+ // We look for the Fly that's in the same Area.
+ // Areas can for now only be Head/Footer or Flys.
+
+ if ( !pAnch ) // If an Anchor was passed along, that one counts (ctor!)
+ pAnch = AnchorFrame();
+
+ SwLayoutFrame *pLay;
+ if ( pAnch->IsInFly() )
+ pLay = pAnch->FindFlyFrame();
+ else
+ {
+ // FindFooterOrHeader is not appropriate here, as we may not have a
+ // connection to the Anchor yet.
+ pLay = pAnch->GetUpper();
+ while ( pLay && !(pLay->GetType() & (SwFrameType::Header|SwFrameType::Footer)) )
+ pLay = pLay->GetUpper();
+ }
+
+ SwIterator<SwFlyFrame,SwFormat> aIter( rChain );
+ SwFlyFrame *pFly = aIter.First();
+ if ( pLay )
+ {
+ while ( pFly )
+ {
+ if ( pFly->GetAnchorFrame() )
+ {
+ if ( pFly->GetAnchorFrame()->IsInFly() )
+ {
+ if ( pFly->AnchorFrame()->FindFlyFrame() == pLay )
+ break;
+ }
+ else if ( pLay == pFly->FindFooterOrHeader() )
+ break;
+ }
+ pFly = aIter.Next();
+ }
+ }
+ else if ( pFly )
+ {
+ OSL_ENSURE( !aIter.Next(), "chain with more than one instance" );
+ }
+ return pFly;
+}
+
+SwFrame *SwFlyFrame::FindLastLower()
+{
+ SwFrame *pRet = ContainsAny();
+ if ( pRet && pRet->IsInTab() )
+ pRet = pRet->FindTabFrame();
+ SwFrame *pNxt = pRet;
+ while ( pNxt && IsAnLower( pNxt ) )
+ { pRet = pNxt;
+ pNxt = pNxt->FindNext();
+ }
+ return pRet;
+}
+
+bool SwFlyFrame::FrameSizeChg( const SwFormatFrameSize &rFrameSize )
+{
+ bool bRet = false;
+ SwTwips nDiffHeight = getFrameArea().Height();
+ if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Variable )
+ mbFixSize = m_bMinHeight = false;
+ else
+ {
+ if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Fixed )
+ {
+ mbFixSize = true;
+ m_bMinHeight = false;
+ }
+ else if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum )
+ {
+ mbFixSize = false;
+ m_bMinHeight = true;
+ }
+ nDiffHeight -= rFrameSize.GetHeight();
+ }
+ // If the Fly contains columns, we already need to set the Fly
+ // and the Columns to the required value or else we run into problems.
+ if ( Lower() )
+ {
+ if ( Lower()->IsColumnFrame() )
+ {
+ const SwRect aOld( GetObjRectWithSpaces() );
+ const Size aOldSz( getFramePrintArea().SSize() );
+ const SwTwips nDiffWidth = getFrameArea().Width() - rFrameSize.GetWidth();
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( aFrm.Height() - nDiffHeight );
+ aFrm.Width ( aFrm.Width() - nDiffWidth );
+ }
+
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( aPrt.Height() - nDiffHeight );
+ aPrt.Width ( aPrt.Width() - nDiffWidth );
+ }
+
+ ChgLowersProp( aOldSz );
+ ::Notify( this, FindPageFrame(), aOld );
+ setFrameAreaPositionValid(false);
+ bRet = true;
+ }
+ else if ( Lower()->IsNoTextFrame() )
+ {
+ mbFixSize = true;
+ m_bMinHeight = false;
+ }
+ }
+ return bRet;
+}
+
+void SwFlyFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SwFlyFrameInvFlags eInvFlags = SwFlyFrameInvFlags::NONE;
+ if(pLegacy->m_pNew && pLegacy->m_pOld && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
+ {
+ SfxItemIter aNIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet());
+ SfxItemIter aOIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet());
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ SwAttrSetChg aOldSet(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld));
+ SwAttrSetChg aNewSet(*static_cast<const SwAttrSetChg*>(pLegacy->m_pNew));
+ do
+ {
+ UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
+ pNItem = aNIter.NextItem();
+ pOItem = aOIter.NextItem();
+ } while(pNItem);
+ if(aOldSet.Count() || aNewSet.Count())
+ SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ else
+ UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if(eInvFlags == SwFlyFrameInvFlags::NONE)
+ return;
+
+ Invalidate_();
+ if(eInvFlags & SwFlyFrameInvFlags::InvalidatePos)
+ {
+ InvalidatePos_();
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+ }
+ if(eInvFlags & SwFlyFrameInvFlags::InvalidateSize)
+ {
+ InvalidateSize_();
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+ }
+ if(eInvFlags & SwFlyFrameInvFlags::InvalidatePrt)
+ InvalidatePrt_();
+ if(eInvFlags & SwFlyFrameInvFlags::SetNotifyBack)
+ SetNotifyBack();
+ if(eInvFlags & SwFlyFrameInvFlags::SetCompletePaint)
+ SetCompletePaint();
+ if((eInvFlags & SwFlyFrameInvFlags::ClearContourCache) && Lower() && Lower()->IsNoTextFrame())
+ ClrContourCache( GetVirtDrawObj() );
+ SwRootFrame *pRoot;
+ if(eInvFlags & SwFlyFrameInvFlags::InvalidateBrowseWidth && nullptr != (pRoot = getRootFrame()))
+ pRoot->InvalidateBrowseWidth();
+ // #i28701#
+ if(eInvFlags & SwFlyFrameInvFlags::UpdateObjInSortedList)
+ {
+ // update sorted object lists, the Writer fly frame is registered at.
+ UpdateObjInSortedList();
+ }
+
+ // #i87645# - reset flags for the layout process (only if something has been invalidated)
+ ResetLayoutProcessBools();
+ }
+ else if (auto pGetZOrdnerHint = dynamic_cast<const sw::GetZOrderHint*>(&rHint))
+ {
+ const auto& rFormat(dynamic_cast<const SwFrameFormat&>(rMod));
+ if (rFormat.Which() == RES_FLYFRMFMT && rFormat.getIDocumentLayoutAccess().GetCurrentViewShell()) // #i11176#
+ pGetZOrdnerHint->m_rnZOrder = GetVirtDrawObj()->GetOrdNum();
+ }
+ else if (auto pConnectedHint = dynamic_cast<const sw::GetObjectConnectedHint*>(&rHint))
+ {
+ const auto& rFormat(dynamic_cast<const SwFrameFormat&>(rMod));
+ if (!pConnectedHint->m_risConnected && rFormat.Which() == RES_FLYFRMFMT && (!pConnectedHint->m_pRoot || pConnectedHint->m_pRoot == getRootFrame()))
+ pConnectedHint->m_risConnected = true;
+ }
+}
+
+void SwFlyFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
+ SwFlyFrameInvFlags &rInvFlags,
+ SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
+{
+ bool bClear = true;
+ const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ switch( nWhich )
+ {
+ case RES_VERT_ORIENT:
+ case RES_HORI_ORIENT:
+ // #i18732# - consider new option 'follow text flow'
+ case RES_FOLLOW_TEXT_FLOW:
+ {
+ // ATTENTION: Always also change Action in ChgRePos()!
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack;
+ }
+ break;
+ // #i28701# - consider new option 'wrap influence on position'
+ case RES_WRAP_INFLUENCE_ON_OBJPOS:
+ {
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack
+ | SwFlyFrameInvFlags::UpdateObjInSortedList;
+ }
+ break;
+ case RES_SURROUND:
+ {
+ //#i28701# - invalidate position on change of
+ // wrapping style.
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::ClearContourCache;
+ // The background needs to be messaged and invalidated
+ const SwRect aTmp( GetObjRectWithSpaces() );
+ NotifyBackground( FindPageFrame(), aTmp, PrepareHint::FlyFrameAttributesChanged );
+
+ // By changing the flow of frame-bound Frames, a vertical alignment
+ // can be activated/deactivated => MakeFlyPos
+ if( RndStdIds::FLY_AT_FLY == GetFormat()->GetAnchor().GetAnchorId() )
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack;
+
+ // Delete contour in the Node if necessary
+ if ( Lower() && Lower()->IsNoTextFrame() &&
+ !GetFormat()->GetSurround().IsContour() )
+ {
+ SwNoTextNode *pNd = static_cast<SwNoTextNode*>(static_cast<SwNoTextFrame*>(Lower())->GetNode());
+ if ( pNd->HasContour() )
+ pNd->SetContour( nullptr );
+ }
+ // #i28701# - perform reorder of object lists
+ // at anchor frame and at page frame.
+ rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
+ }
+ break;
+
+ case RES_PROTECT:
+ if (pNew)
+ {
+ const SvxProtectItem *pP = static_cast<const SvxProtectItem*>(pNew);
+ GetVirtDrawObj()->SetMoveProtect( pP->IsPosProtected() );
+ GetVirtDrawObj()->SetResizeProtect( pP->IsSizeProtected() );
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( pSh )
+ {
+ SwRootFrame* pLayout = getRootFrame();
+ if( pLayout && pLayout->IsAnyShellAccessible() )
+ pSh->Imp()->InvalidateAccessibleEditableState( true, this );
+ }
+#endif
+ }
+ break;
+ case RES_COL:
+ if (pOld && pNew)
+ {
+ ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
+ const SwFormatFrameSize &rNew = GetFormat()->GetFrameSize();
+ if ( FrameSizeChg( rNew ) )
+ NotifyDrawObj();
+ rInvFlags |= SwFlyFrameInvFlags::InvalidateSize | SwFlyFrameInvFlags::SetNotifyBack
+ | SwFlyFrameInvFlags::SetCompletePaint;
+ }
+ break;
+
+ case RES_FRM_SIZE:
+ case RES_FMT_CHG:
+ {
+ const SwFormatFrameSize &rNew = GetFormat()->GetFrameSize();
+ if ( FrameSizeChg( rNew ) )
+ NotifyDrawObj();
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::InvalidateSize
+ | SwFlyFrameInvFlags::InvalidatePrt | SwFlyFrameInvFlags::SetNotifyBack
+ | SwFlyFrameInvFlags::SetCompletePaint
+ | SwFlyFrameInvFlags::InvalidateBrowseWidth
+ | SwFlyFrameInvFlags::ClearContourCache;
+ if (pOld && RES_FMT_CHG == nWhich)
+ {
+ SwRect aNew( GetObjRectWithSpaces() );
+ SwRect aOld( getFrameArea() );
+ const SvxULSpaceItem &rUL = static_cast<const SwFormatChg*>(pOld)->pChangedFormat->GetULSpace();
+ aOld.Top( std::max( aOld.Top() - tools::Long(rUL.GetUpper()), tools::Long(0) ) );
+ aOld.AddHeight(rUL.GetLower() );
+ const SvxLRSpaceItem &rLR = static_cast<const SwFormatChg*>(pOld)->pChangedFormat->GetLRSpace();
+ aOld.Left ( std::max( aOld.Left() - rLR.GetLeft(), tools::Long(0) ) );
+ aOld.AddWidth(rLR.GetRight() );
+ aNew.Union( aOld );
+ NotifyBackground( FindPageFrame(), aNew, PrepareHint::Clear );
+
+ // Special case:
+ // When assigning a template we cannot rely on the old column
+ // attribute. As there need to be at least enough for ChgColumns,
+ // we need to create a temporary attribute.
+ SwFormatCol aCol;
+ if ( Lower() && Lower()->IsColumnFrame() )
+ {
+ sal_uInt16 nCol = 0;
+ SwFrame *pTmp = Lower();
+ do
+ { ++nCol;
+ pTmp = pTmp->GetNext();
+ } while ( pTmp );
+ aCol.Init( nCol, 0, 1000 );
+ }
+ ChgColumns( aCol, GetFormat()->GetCol() );
+ }
+
+ SwFormatURL aURL( GetFormat()->GetURL() );
+
+ SwFormatFrameSize *pNewFormatFrameSize = nullptr;
+ SwFormatChg *pOldFormatChg = nullptr;
+ if (nWhich == RES_FRM_SIZE)
+ pNewFormatFrameSize = const_cast<SwFormatFrameSize*>(static_cast<const SwFormatFrameSize*>(pNew));
+ else
+ pOldFormatChg = const_cast<SwFormatChg*>(static_cast<const SwFormatChg*>(pOld));
+
+ if (aURL.GetMap() && (pNewFormatFrameSize || pOldFormatChg))
+ {
+ const SwFormatFrameSize &rOld = pNewFormatFrameSize ?
+ *pNewFormatFrameSize :
+ pOldFormatChg->pChangedFormat->GetFrameSize();
+ //#35091# Can be "times zero", when loading the template
+ if ( rOld.GetWidth() && rOld.GetHeight() )
+ {
+
+ Fraction aScaleX( rOld.GetWidth(), rNew.GetWidth() );
+ Fraction aScaleY( rOld.GetHeight(), rOld.GetHeight() );
+ aURL.GetMap()->Scale( aScaleX, aScaleY );
+ SwFrameFormat *pFormat = GetFormat();
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aURL );
+ pFormat->UnlockModify();
+ }
+ }
+ const SvxProtectItem &rP = GetFormat()->GetProtect();
+ GetVirtDrawObj()->SetMoveProtect( rP.IsPosProtected() );
+ GetVirtDrawObj()->SetResizeProtect( rP.IsSizeProtected() );
+
+ if ( pSh )
+ pSh->InvalidateWindows( getFrameArea() );
+ const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
+ const SdrLayerID nId = GetFormat()->GetOpaque().GetValue() ?
+ rIDDMA.GetHeavenId() :
+ rIDDMA.GetHellId();
+ GetVirtDrawObj()->SetLayer( nId );
+
+ if ( Lower() )
+ {
+ // Delete contour in the Node if necessary
+ if( Lower()->IsNoTextFrame() &&
+ !GetFormat()->GetSurround().IsContour() )
+ {
+ SwNoTextNode *pNd = static_cast<SwNoTextNode*>(static_cast<SwNoTextFrame*>(Lower())->GetNode());
+ if ( pNd->HasContour() )
+ pNd->SetContour( nullptr );
+ }
+ else if( !Lower()->IsColumnFrame() )
+ {
+ SwFrame* pFrame = GetLastLower();
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ }
+ }
+
+ // #i28701# - perform reorder of object lists
+ // at anchor frame and at page frame.
+ rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
+
+ break;
+ }
+ case RES_UL_SPACE:
+ case RES_LR_SPACE:
+ {
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::ClearContourCache;
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ getRootFrame()->InvalidateBrowseWidth();
+ SwRect aNew( GetObjRectWithSpaces() );
+ SwRect aOld( getFrameArea() );
+ if (pNew)
+ {
+ if ( RES_UL_SPACE == nWhich )
+ {
+ const SvxULSpaceItem &rUL = *static_cast<const SvxULSpaceItem*>(pNew);
+ aOld.Top( std::max( aOld.Top() - tools::Long(rUL.GetUpper()), tools::Long(0) ) );
+ aOld.AddHeight(rUL.GetLower() );
+ }
+ else
+ {
+ const SvxLRSpaceItem &rLR = *static_cast<const SvxLRSpaceItem*>(pNew);
+ aOld.Left ( std::max( aOld.Left() - rLR.GetLeft(), tools::Long(0) ) );
+ aOld.AddWidth(rLR.GetRight() );
+ }
+ }
+ aNew.Union( aOld );
+ NotifyBackground( FindPageFrame(), aNew, PrepareHint::Clear );
+ }
+ break;
+
+ case RES_TEXT_VERT_ADJUST:
+ {
+ InvalidateContentPos();
+ rInvFlags |= SwFlyFrameInvFlags::SetCompletePaint;
+ }
+ break;
+
+ case RES_BOX:
+ case RES_SHADOW:
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::InvalidateSize
+ | SwFlyFrameInvFlags::InvalidatePrt | SwFlyFrameInvFlags::SetCompletePaint;
+ break;
+
+ case RES_FRAMEDIR :
+ SetDerivedVert( false );
+ SetDerivedR2L( false );
+ CheckDirChange();
+ break;
+
+ case RES_OPAQUE:
+ if (pNew)
+ {
+ if ( pSh )
+ pSh->InvalidateWindows( getFrameArea() );
+
+ const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
+ const SdrLayerID nId = static_cast<const SvxOpaqueItem*>(pNew)->GetValue() ?
+ rIDDMA.GetHeavenId() :
+ rIDDMA.GetHellId();
+ GetVirtDrawObj()->SetLayer( nId );
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( pSh )
+ {
+ SwRootFrame* pLayout = getRootFrame();
+ if( pLayout && pLayout->IsAnyShellAccessible() )
+ {
+ pSh->Imp()->DisposeAccessibleFrame( this );
+ pSh->Imp()->AddAccessibleFrame( this );
+ }
+ }
+#endif
+ // #i28701# - perform reorder of object lists
+ // at anchor frame and at page frame.
+ rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
+ }
+ break;
+
+ case RES_URL:
+ // The interface changes the frame size when interacting with text frames,
+ // the Map, however, needs to be relative to FrameSize().
+ if ( (!Lower() || !Lower()->IsNoTextFrame()) && pNew && pOld &&
+ static_cast<const SwFormatURL*>(pNew)->GetMap() && static_cast<const SwFormatURL*>(pOld)->GetMap() )
+ {
+ const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
+ if ( rSz.GetHeight() != getFrameArea().Height() ||
+ rSz.GetWidth() != getFrameArea().Width() )
+ {
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ Fraction aScaleX( getFrameArea().Width(), rSz.GetWidth() );
+ Fraction aScaleY( getFrameArea().Height(), rSz.GetHeight() );
+ aURL.GetMap()->Scale( aScaleX, aScaleY );
+ SwFrameFormat *pFormat = GetFormat();
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aURL );
+ pFormat->UnlockModify();
+ }
+ }
+ // No invalidation necessary
+ break;
+
+ case RES_CHAIN:
+ if (pNew)
+ {
+ const SwFormatChain *pChain = static_cast<const SwFormatChain*>(pNew);
+ if ( pChain->GetNext() )
+ {
+ SwFlyFrame *pFollow = FindChainNeighbour( *pChain->GetNext() );
+ if ( GetNextLink() && pFollow != GetNextLink() )
+ SwFlyFrame::UnchainFrames( this, GetNextLink());
+ if ( pFollow )
+ {
+ if ( pFollow->GetPrevLink() &&
+ pFollow->GetPrevLink() != this )
+ SwFlyFrame::UnchainFrames( pFollow->GetPrevLink(),
+ pFollow );
+ if ( !GetNextLink() )
+ SwFlyFrame::ChainFrames( this, pFollow );
+ }
+ }
+ else if ( GetNextLink() )
+ SwFlyFrame::UnchainFrames( this, GetNextLink() );
+ if ( pChain->GetPrev() )
+ {
+ SwFlyFrame *pMaster = FindChainNeighbour( *pChain->GetPrev() );
+ if ( GetPrevLink() && pMaster != GetPrevLink() )
+ SwFlyFrame::UnchainFrames( GetPrevLink(), this );
+ if ( pMaster )
+ {
+ if ( pMaster->GetNextLink() &&
+ pMaster->GetNextLink() != this )
+ SwFlyFrame::UnchainFrames( pMaster,
+ pMaster->GetNextLink() );
+ if ( !GetPrevLink() )
+ SwFlyFrame::ChainFrames( pMaster, this );
+ }
+ }
+ else if ( GetPrevLink() )
+ SwFlyFrame::UnchainFrames( GetPrevLink(), this );
+ }
+ [[fallthrough]];
+ default:
+ bClear = false;
+ }
+ if ( !bClear )
+ return;
+
+ if ( pOldSet || pNewSet )
+ {
+ if ( pOldSet )
+ pOldSet->ClearItem( nWhich );
+ if ( pNewSet )
+ pNewSet->ClearItem( nWhich );
+ }
+ else
+ {
+ SwModify aMod;
+ SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ }
+}
+
+/// Gets information from the Modify
+bool SwFlyFrame::GetInfo( SfxPoolItem & rInfo ) const
+{
+ if( RES_AUTOFMT_DOCNODE == rInfo.Which() )
+ return false; // There's a FlyFrame, so use it
+ return true; // Continue searching
+}
+
+void SwFlyFrame::Invalidate_( SwPageFrame const *pPage )
+{
+ InvalidatePage( pPage );
+ m_bNotifyBack = m_bInvalid = true;
+
+ SwFlyFrame *pFrame;
+ if ( GetAnchorFrame() && nullptr != (pFrame = AnchorFrame()->FindFlyFrame()) )
+ {
+ // Very bad case: If the Fly is bound within another Fly which
+ // contains columns, the Format should be from that one.
+ if ( !pFrame->IsLocked() && !pFrame->IsColLocked() &&
+ pFrame->Lower() && pFrame->Lower()->IsColumnFrame() )
+ pFrame->InvalidateSize();
+ }
+
+ // #i85216#
+ // if vertical position is oriented at a layout frame inside a ghost section,
+ // assure that the position is invalidated and that the information about
+ // the vertical position oriented frame is cleared
+ if ( GetVertPosOrientFrame() && GetVertPosOrientFrame()->IsLayoutFrame() )
+ {
+ const SwSectionFrame* pSectFrame( GetVertPosOrientFrame()->FindSctFrame() );
+ if ( pSectFrame && pSectFrame->GetSection() == nullptr )
+ {
+ InvalidatePos();
+ ClearVertPosOrientFrame();
+ }
+ }
+}
+
+/** Change the relative position
+ *
+ * The position will be Fix automatically and the attribute is changed accordingly.
+ */
+void SwFlyFrame::ChgRelPos( const Point &rNewPos )
+{
+ if ( GetCurrRelPos() == rNewPos )
+ return;
+
+ SwFrameFormat *pFormat = GetFormat();
+ const bool bVert = GetAnchorFrame()->IsVertical();
+ const SwTwips nNewY = bVert ? rNewPos.X() : rNewPos.Y();
+ SwTwips nTmpY = nNewY == LONG_MAX ? 0 : nNewY;
+ if( bVert )
+ nTmpY = -nTmpY;
+ SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT> aSet( pFormat->GetDoc()->GetAttrPool() );
+
+ SwFormatVertOrient aVert( pFormat->GetVertOrient() );
+ const SwTextFrame *pAutoFrame = nullptr;
+ // #i34948# - handle also at-page and at-fly anchored
+ // Writer fly frames
+ const RndStdIds eAnchorType = GetFrameFormat().GetAnchor().GetAnchorId();
+ if ( eAnchorType == RndStdIds::FLY_AT_PAGE )
+ {
+ aVert.SetVertOrient( text::VertOrientation::NONE );
+ aVert.SetRelationOrient( text::RelOrientation::PAGE_FRAME );
+ }
+ else if ( eAnchorType == RndStdIds::FLY_AT_FLY )
+ {
+ aVert.SetVertOrient( text::VertOrientation::NONE );
+ aVert.SetRelationOrient( text::RelOrientation::FRAME );
+ }
+ else if ( IsFlyAtContentFrame() || text::VertOrientation::NONE != aVert.GetVertOrient() )
+ {
+ if( text::RelOrientation::CHAR == aVert.GetRelationOrient() && IsAutoPos() )
+ {
+ if( LONG_MAX != nNewY )
+ {
+ aVert.SetVertOrient( text::VertOrientation::NONE );
+ assert(GetAnchorFrame()->IsTextFrame());
+ pAutoFrame = static_cast<const SwTextFrame*>(GetAnchorFrame());
+ TextFrameIndex const nOfs(pAutoFrame->MapModelToViewPos(
+ *pFormat->GetAnchor().GetContentAnchor()));
+ while( pAutoFrame->GetFollow() &&
+ pAutoFrame->GetFollow()->GetOffset() <= nOfs )
+ {
+ if( pAutoFrame == GetAnchorFrame() )
+ nTmpY += pAutoFrame->GetRelPos().Y();
+ nTmpY -= pAutoFrame->GetUpper()->getFramePrintArea().Height();
+ pAutoFrame = pAutoFrame->GetFollow();
+ }
+ nTmpY = static_cast<SwFlyAtContentFrame*>(this)->GetRelCharY(pAutoFrame)-nTmpY;
+ }
+ else
+ aVert.SetVertOrient( text::VertOrientation::CHAR_BOTTOM );
+ }
+ else
+ {
+ aVert.SetVertOrient( text::VertOrientation::NONE );
+ aVert.SetRelationOrient( text::RelOrientation::FRAME );
+ }
+ }
+ aVert.SetPos( nTmpY );
+ aSet.Put( aVert );
+
+ // For Flys in the Cnt, the horizontal orientation is of no interest,
+ // as it's always 0
+ if ( !IsFlyInContentFrame() )
+ {
+ const SwTwips nNewX = bVert ? rNewPos.Y() : rNewPos.X();
+ SwTwips nTmpX = nNewX == LONG_MAX ? 0 : nNewX;
+ SwFormatHoriOrient aHori( pFormat->GetHoriOrient() );
+ // #i34948# - handle also at-page and at-fly anchored
+ // Writer fly frames
+ if ( eAnchorType == RndStdIds::FLY_AT_PAGE )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::NONE );
+ aHori.SetRelationOrient( text::RelOrientation::PAGE_FRAME );
+ aHori.SetPosToggle( false );
+ }
+ else if ( eAnchorType == RndStdIds::FLY_AT_FLY )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::NONE );
+ aHori.SetRelationOrient( text::RelOrientation::FRAME );
+ aHori.SetPosToggle( false );
+ }
+ else if ( IsFlyAtContentFrame() || text::HoriOrientation::NONE != aHori.GetHoriOrient() )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::NONE );
+ if( text::RelOrientation::CHAR == aHori.GetRelationOrient() && IsAutoPos() )
+ {
+ if( LONG_MAX != nNewX )
+ {
+ if( !pAutoFrame )
+ {
+ assert(GetAnchorFrame()->IsTextFrame());
+ pAutoFrame = static_cast<const SwTextFrame*>(GetAnchorFrame());
+ TextFrameIndex const nOfs(pAutoFrame->MapModelToViewPos(
+ *pFormat->GetAnchor().GetContentAnchor()));
+ while( pAutoFrame->GetFollow() &&
+ pAutoFrame->GetFollow()->GetOffset() <= nOfs )
+ pAutoFrame = pAutoFrame->GetFollow();
+ }
+ nTmpX -= static_cast<SwFlyAtContentFrame*>(this)->GetRelCharX(pAutoFrame);
+ }
+ }
+ else
+ aHori.SetRelationOrient( text::RelOrientation::FRAME );
+ aHori.SetPosToggle( false );
+ }
+ aHori.SetPos( nTmpX );
+ aSet.Put( aHori );
+ }
+ SetCurrRelPos( rNewPos );
+ pFormat->GetDoc()->SetAttr( aSet, *pFormat );
+
+}
+
+/** "Formats" the Frame; Frame and PrtArea.
+ *
+ * The FixSize is not inserted here.
+ */
+void SwFlyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
+{
+ OSL_ENSURE( pAttrs, "FlyFrame::Format, pAttrs is 0." );
+
+ ColLock();
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ if ( getFrameArea().Top() == FAR_AWAY && getFrameArea().Left() == FAR_AWAY )
+ {
+ // Remove safety switch (see SwFrame::CTor)
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(0);
+ aFrm.Pos().setY(0);
+ }
+
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+ }
+
+ // Check column width and set it if needed
+ if ( Lower() && Lower()->IsColumnFrame() )
+ AdjustColumns( nullptr, false );
+
+ setFrameAreaSizeValid(true);
+
+ const SwTwips nUL = pAttrs->CalcTopLine() + pAttrs->CalcBottomLine();
+ const SwTwips nLR = pAttrs->CalcLeftLine() + pAttrs->CalcRightLine();
+ const SwFormatFrameSize &rFrameSz = GetFormat()->GetFrameSize();
+ Size aRelSize( CalcRel( rFrameSz ) );
+
+ OSL_ENSURE( pAttrs->GetSize().Height() != 0 || rFrameSz.GetHeightPercent(), "FrameAttr height is 0." );
+ OSL_ENSURE( pAttrs->GetSize().Width() != 0 || rFrameSz.GetWidthPercent(), "FrameAttr width is 0." );
+
+ SwRectFnSet aRectFnSet(this);
+ if( !HasFixSize() )
+ {
+ tools::Long nMinHeight = 0;
+ if( IsMinHeight() )
+ nMinHeight = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
+
+ SwTwips nRemaining = CalcContentHeight(pAttrs, nMinHeight, nUL);
+ if( IsMinHeight() && (nRemaining + nUL) < nMinHeight )
+ nRemaining = nMinHeight - nUL;
+ // Because the Grow/Shrink of the Flys does not directly
+ // set the size - only indirectly by triggering a Format()
+ // via Invalidate() - the sizes need to be set here.
+ // Notification is running along already.
+ // As we already got a lot of zeros per attribute, we block them
+ // from now on.
+
+ if ( nRemaining < MINFLY )
+ nRemaining = MINFLY;
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nRemaining );
+ }
+
+ nRemaining -= aRectFnSet.GetHeight(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nRemaining + nUL );
+ }
+
+ // #i68520#
+ if ( nRemaining + nUL != 0 )
+ {
+ InvalidateObjRectWithSpaces();
+ }
+
+ setFrameAreaSizeValid(true);
+
+ if (SwFrameFormat* pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT))
+ {
+ // This fly is a textbox of a draw shape.
+ SdrObject* pShape = pShapeFormat->FindSdrObject();
+ if (SdrObjCustomShape* pCustomShape = dynamic_cast<SdrObjCustomShape*>( pShape) )
+ {
+ // The shape is a customshape: then inform it about the calculated fly size.
+ Size aSize(getFrameArea().Width(), getFrameArea().Height());
+ pCustomShape->SuggestTextFrameSize(aSize);
+ // Do the calculations normally done after touching editeng text of the shape.
+ pCustomShape->NbcSetOutlinerParaObjectForText(std::nullopt, nullptr);
+ }
+ }
+ }
+ else
+ {
+ // Fixed Frames do not Format itself
+ setFrameAreaSizeValid(true);
+
+ // Flys set their size using the attr
+ SwTwips nNewSize = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
+ nNewSize -= nUL;
+ if( nNewSize < MINFLY )
+ nNewSize = MINFLY;
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nNewSize );
+ }
+
+ nNewSize += nUL - aRectFnSet.GetHeight(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nNewSize );
+ }
+
+ // #i68520#
+ if ( nNewSize != 0 )
+ {
+ InvalidateObjRectWithSpaces();
+ }
+ }
+
+ if ( !m_bFormatHeightOnly )
+ {
+ OSL_ENSURE( aRelSize == CalcRel( rFrameSz ), "SwFlyFrame::Format CalcRel problem" );
+ SwTwips nNewSize = aRectFnSet.IsVert() ? aRelSize.Height() : aRelSize.Width();
+
+ if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
+ {
+ // #i9046# Autowidth for fly frames
+ const SwTwips nAutoWidth = lcl_CalcAutoWidth( *this );
+ if ( nAutoWidth )
+ {
+ if( SwFrameSize::Minimum == rFrameSz.GetWidthSizeType() )
+ nNewSize = std::max( nNewSize - nLR, nAutoWidth );
+ else
+ nNewSize = nAutoWidth;
+ }
+ }
+ else
+ nNewSize -= nLR;
+
+ if( nNewSize < MINFLY )
+ nNewSize = MINFLY;
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetWidth( aPrt, nNewSize );
+ }
+
+ nNewSize += nLR - aRectFnSet.GetWidth(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddRight( aFrm, nNewSize );
+ }
+
+ // #i68520#
+ if ( nNewSize != 0 )
+ {
+ InvalidateObjRectWithSpaces();
+ }
+ }
+ }
+ ColUnlock();
+}
+
+// #i11760# - change parameter <bNoColl>: type <bool>;
+// add new parameter <bNoCalcFollow> with
+// new parameter <bNoCalcFollow> was used by method
+// <FormatWidthCols(..)> to avoid follow formatting
+// for text frames. But, unformatted follows causes
+// problems in method <SwContentFrame::WouldFit_(..)>,
+// which assumes that the follows are formatted.
+// Thus, <bNoCalcFollow> no longer used by <FormatWidthCols(..)>.
+void CalcContent( SwLayoutFrame *pLay, bool bNoColl )
+{
+ vcl::RenderContext* pRenderContext = pLay->getRootFrame()->GetCurrShell()->GetOut();
+ SwSectionFrame* pSect;
+ bool bCollect = false;
+ if( pLay->IsSctFrame() )
+ {
+ pSect = static_cast<SwSectionFrame*>(pLay);
+ if( pSect->IsEndnAtEnd() && !bNoColl )
+ {
+ bCollect = true;
+ SwLayouter::CollectEndnotes( pLay->GetFormat()->GetDoc(), pSect );
+ }
+ pSect->CalcFootnoteContent();
+ }
+ else
+ pSect = nullptr;
+ SwFrame *pFrame = pLay->ContainsAny();
+ if ( !pFrame )
+ {
+ if( pSect )
+ {
+ if( pSect->HasFollow() )
+ pFrame = pSect->GetFollow()->ContainsAny();
+ if( !pFrame )
+ {
+ if( pSect->IsEndnAtEnd() )
+ {
+ if( bCollect )
+ pLay->GetFormat()->GetDoc()->getIDocumentLayoutAccess().GetLayouter()->
+ InsertEndnotes( pSect );
+ bool bLock = pSect->IsFootnoteLock();
+ pSect->SetFootnoteLock( true );
+ pSect->CalcFootnoteContent();
+ pSect->CalcFootnoteContent();
+ pSect->SetFootnoteLock( bLock );
+ }
+ return;
+ }
+ pFrame->InvalidatePos_();
+ }
+ else
+ return;
+ }
+ pFrame->InvalidatePage();
+
+ do
+ {
+ // local variables to avoid loops caused by anchored object positioning
+ SwAnchoredObject* pAgainObj1 = nullptr;
+ SwAnchoredObject* pAgainObj2 = nullptr;
+
+ // FME 2007-08-30 #i81146# new loop control
+ int nLoopControlRuns = 0;
+ // tdf#152106 loop control for multi-column sections
+ int nLoopControlRunsInMultiCol = 0;
+ const int nLoopControlMax = 20;
+ const SwFrame* pLoopControlCond = nullptr;
+
+ SwFrame* pLast;
+ do
+ {
+ pLast = pFrame;
+ bool const wasFrameLowerOfLay(pLay->IsAnLower(pFrame));
+ if( pFrame->IsVertical() ?
+ ( pFrame->GetUpper()->getFramePrintArea().Height() != pFrame->getFrameArea().Height() )
+ : ( pFrame->GetUpper()->getFramePrintArea().Width() != pFrame->getFrameArea().Width() ) )
+ {
+ pFrame->Prepare( PrepareHint::FixSizeChanged );
+ pFrame->InvalidateSize_();
+ }
+
+ if ( pFrame->IsTabFrame() )
+ {
+ static_cast<SwTabFrame*>(pFrame)->m_bCalcLowers = true;
+ // #i18103# - lock move backward of follow table,
+ // if no section content is formatted or follow table belongs
+ // to the section, which content is formatted.
+ if ( static_cast<SwTabFrame*>(pFrame)->IsFollow() &&
+ ( !pSect || pSect == pFrame->FindSctFrame() ) )
+ {
+ static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove = true;
+ }
+ }
+
+ {
+ SwFrameDeleteGuard aDeletePageGuard(pSect ? pSect->FindPageFrame() : nullptr);
+ SwFrameDeleteGuard aDeleteGuard(pSect);
+ pFrame->Calc(pRenderContext);
+ }
+
+ // #i11760# - reset control flag for follow format.
+ if ( pFrame->IsTextFrame() )
+ {
+ static_cast<SwTextFrame*>(pFrame)->AllowFollowFormat();
+ }
+
+ // The keep-attribute can cause the position
+ // of the prev to be invalid:
+ // Do not consider invalid previous frame
+ // due to its keep-attribute, if current frame is a follow or is locked.
+ // #i44049# - do not consider invalid previous
+ // frame due to its keep-attribute, if it can't move forward.
+ // #i57765# - do not consider invalid previous
+ // frame, if current frame has a column/page break before attribute.
+ SwFrame* pTmpPrev = pFrame->FindPrev();
+ SwFlowFrame* pTmpPrevFlowFrame = pTmpPrev && pTmpPrev->IsFlowFrame() ? SwFlowFrame::CastFlowFrame(pTmpPrev) : nullptr;
+ SwFlowFrame* pTmpFlowFrame = pFrame->IsFlowFrame() ? SwFlowFrame::CastFlowFrame(pFrame) : nullptr;
+
+ bool bPrevInvalid = pTmpPrevFlowFrame && pTmpFlowFrame &&
+ !pTmpFlowFrame->IsFollow() &&
+ !StackHack::IsLocked() && // #i76382#
+ !pTmpFlowFrame->IsJoinLocked() &&
+ !pTmpPrev->isFrameAreaPositionValid() &&
+ pLay->IsAnLower( pTmpPrev ) &&
+ pTmpPrevFlowFrame->IsKeep(pTmpPrev->GetAttrSet()->GetKeep(), pTmpPrev->GetBreakItem()) &&
+ pTmpPrevFlowFrame->IsKeepFwdMoveAllowed();
+
+ // format floating screen objects anchored to the frame.
+ if ( !bPrevInvalid && pFrame->GetDrawObjs() && pLay->IsAnLower( pFrame ) )
+ {
+ bool bAgain = false;
+ bool bRestartLayoutProcess = false;
+ size_t nCnt = pFrame->GetDrawObjs()->size();
+ size_t i = 0;
+ while ( i < nCnt )
+ {
+ // pFrame can move to a different page in FormatObj()
+ SwPageFrame *const pPageFrame = pFrame->FindPageFrame();
+
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
+ assert(pAnchoredObj);
+
+ // determine if anchored object has to be
+ // formatted and, in case, format it
+ if ( !pAnchoredObj->PositionLocked() && pAnchoredObj->IsFormatPossible() )
+ {
+ // #i43737# - no invalidation of
+ // anchored object needed - causes loops for as-character
+ // anchored objects.
+ //pAnchoredObj->InvalidateObjPos();
+ SwRect aRect( pAnchoredObj->GetObjRect() );
+ if ( !SwObjectFormatter::FormatObj( *pAnchoredObj, pFrame, pPageFrame ) )
+ {
+ bRestartLayoutProcess = true;
+ break;
+ }
+ // #i3317# - restart layout process,
+ // if the position of the anchored object is locked now.
+ if ( pAnchoredObj->PositionLocked() )
+ {
+ bRestartLayoutProcess = true;
+ break;
+ }
+
+ if ( aRect != pAnchoredObj->GetObjRect() )
+ {
+ bAgain = true;
+ if ( pAgainObj2 == pAnchoredObj )
+ {
+ OSL_FAIL( "::CalcContent(..) - loop detected, perform attribute changes to avoid the loop" );
+ // Prevent oscillation
+ SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
+ SwFormatSurround aAttr( rFormat.GetSurround() );
+ if( css::text::WrapTextMode_THROUGH != aAttr.GetSurround() )
+ {
+ // When on auto position, we can only set it to
+ // flow through
+ if ((rFormat.GetAnchor().GetAnchorId() ==
+ RndStdIds::FLY_AT_CHAR) &&
+ (css::text::WrapTextMode_PARALLEL ==
+ aAttr.GetSurround()))
+ {
+ aAttr.SetSurround( css::text::WrapTextMode_THROUGH );
+ }
+ else
+ {
+ aAttr.SetSurround( css::text::WrapTextMode_PARALLEL );
+ }
+ rFormat.LockModify();
+ rFormat.SetFormatAttr( aAttr );
+ rFormat.UnlockModify();
+ }
+ }
+ else
+ {
+ if ( pAgainObj1 == pAnchoredObj )
+ pAgainObj2 = pAnchoredObj;
+ pAgainObj1 = pAnchoredObj;
+ }
+ }
+
+ if ( !pFrame->GetDrawObjs() )
+ break;
+ if ( pFrame->GetDrawObjs()->size() < nCnt )
+ {
+ --nCnt;
+ // Do not increment index, in this case
+ continue;
+ }
+ }
+ ++i;
+ }
+
+ // #i28701# - restart layout process, if
+ // requested by floating screen object formatting
+ if (bRestartLayoutProcess
+ // tdf#152106 loop control in multi-column sections to avoid of freezing
+ && nLoopControlRunsInMultiCol < nLoopControlMax
+ // tdf#142080 if it was already on next page, and still is,
+ // ignore restart, as restart could cause infinite loop
+ && (wasFrameLowerOfLay || pLay->IsAnLower(pFrame)))
+ {
+ bool bIsMultiColumn = pSect && pSect->GetSection() && pSect->Lower() &&
+ pSect->Lower()->IsColumnFrame() && pSect->Lower()->GetNext();
+ if ( bIsMultiColumn )
+ ++nLoopControlRunsInMultiCol;
+ pFrame = pLay->ContainsAny();
+ pAgainObj1 = nullptr;
+ pAgainObj2 = nullptr;
+ continue;
+ }
+
+ // #i28701# - format anchor frame after its objects
+ // are formatted, if the wrapping style influence has to be considered.
+ if ( pLay->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ pFrame->Calc(pRenderContext);
+ }
+
+ if ( bAgain )
+ {
+ pFrame = pLay->ContainsContent();
+ if ( pFrame && pFrame->IsInTab() )
+ pFrame = pFrame->FindTabFrame();
+ if( pFrame && pFrame->IsInSct() )
+ {
+ SwSectionFrame* pTmp = pFrame->FindSctFrame();
+ if( pTmp != pLay && pLay->IsAnLower( pTmp ) )
+ pFrame = pTmp;
+ }
+
+ if ( pFrame == pLoopControlCond )
+ ++nLoopControlRuns;
+ else
+ {
+ nLoopControlRuns = 0;
+ pLoopControlCond = pFrame;
+ }
+
+ if ( nLoopControlRuns < nLoopControlMax )
+ continue;
+
+ OSL_FAIL( "LoopControl in CalcContent" );
+ }
+ }
+ if ( pFrame->IsTabFrame() )
+ {
+ if ( static_cast<SwTabFrame*>(pFrame)->IsFollow() )
+ static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove = false;
+ }
+
+ pFrame = bPrevInvalid ? pTmpPrev : pFrame->FindNext();
+ if( !bPrevInvalid && pFrame && pFrame->IsSctFrame() && pSect )
+ {
+ // Empty SectionFrames could be present here
+ while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
+ pFrame = pFrame->FindNext();
+
+ // If FindNext returns the Follow of the original Area, we want to
+ // continue with this content as long as it flows back.
+ if( pFrame && pFrame->IsSctFrame() && ( pFrame == pSect->GetFollow() ||
+ static_cast<SwSectionFrame*>(pFrame)->IsAnFollow( pSect ) ) )
+ {
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pFrame )
+ pFrame->InvalidatePos_();
+ }
+ }
+ // Stay in the pLay.
+ // Except for SectionFrames with Follow: the first ContentFrame of the
+ // Follow will be formatted, so that it gets a chance to move back
+ // into the pLay. Continue as long as these Frames land in pLay.
+ } while ( pFrame &&
+ ( pLay->IsAnLower( pFrame ) ||
+ ( pSect &&
+ ( ( pSect->HasFollow() &&
+ ( pLay->IsAnLower( pLast ) ||
+ ( pLast->IsInSct() &&
+ pLast->FindSctFrame()->IsAnFollow(pSect) ) ) &&
+ pSect->GetFollow()->IsAnLower( pFrame ) ) ||
+ ( pFrame->IsInSct() &&
+ pFrame->FindSctFrame()->IsAnFollow( pSect ) ) ) ) ) );
+ if( pSect )
+ {
+ if( bCollect )
+ {
+ pLay->GetFormat()->GetDoc()->getIDocumentLayoutAccess().GetLayouter()->InsertEndnotes(pSect);
+ pSect->CalcFootnoteContent();
+ }
+ if( pSect->HasFollow() )
+ {
+ SwSectionFrame* pNxt = pSect->GetFollow();
+ while( pNxt && !pNxt->ContainsContent() )
+ pNxt = pNxt->GetFollow();
+ if( pNxt )
+ pNxt->CalcFootnoteContent();
+ }
+ if( bCollect )
+ {
+ pFrame = pLay->ContainsAny();
+ bCollect = false;
+ if( pFrame )
+ continue;
+ }
+ }
+ break;
+ }
+ while( true );
+}
+
+void SwFlyFrame::MakeObjPos()
+{
+ if ( isFrameAreaPositionValid() )
+ return;
+
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ setFrameAreaPositionValid(true);
+
+ // use new class to position object
+ GetAnchorFrame()->Calc(pRenderContext);
+ objectpositioning::SwToLayoutAnchoredObjectPosition
+ aObjPositioning( *GetVirtDrawObj() );
+ aObjPositioning.CalcPosition();
+
+ // #i58280#
+ // update relative position
+ SetCurrRelPos( aObjPositioning.GetRelPos() );
+
+ {
+ SwRectFnSet aRectFnSet(GetAnchorFrame());
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos( aObjPositioning.GetRelPos() );
+ aFrm.Pos() += aRectFnSet.GetPos(GetAnchorFrame()->getFrameArea());
+ }
+
+ // #i69335#
+ InvalidateObjRectWithSpaces();
+}
+
+void SwFlyFrame::MakePrtArea( const SwBorderAttrs &rAttrs )
+{
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+
+ // consider vertical layout
+ SwRectFnSet aRectFnSet(this);
+ aRectFnSet.SetXMargins( *this, rAttrs.CalcLeftLine(),
+ rAttrs.CalcRightLine() );
+ aRectFnSet.SetYMargins( *this, rAttrs.CalcTopLine(),
+ rAttrs.CalcBottomLine() );
+ }
+}
+
+void SwFlyFrame::MakeContentPos( const SwBorderAttrs &rAttrs )
+{
+ if ( m_bValidContentPos )
+ return;
+
+ m_bValidContentPos = true;
+
+ const SwTwips nUL = rAttrs.CalcTopLine() + rAttrs.CalcBottomLine();
+ Size aRelSize( CalcRel( GetFormat()->GetFrameSize() ) );
+
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nMinHeight = 0;
+ if( IsMinHeight() )
+ nMinHeight = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
+
+ Point aNewContentPos = getFramePrintArea().Pos();
+ const SdrTextVertAdjust nAdjust = GetFormat()->GetTextVertAdjust().GetValue();
+
+ if( nAdjust != SDRTEXTVERTADJUST_TOP )
+ {
+ const SwTwips nContentHeight = CalcContentHeight(&rAttrs, nMinHeight, nUL);
+ SwTwips nDiff = 0;
+
+ if( nContentHeight != 0)
+ nDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nContentHeight;
+
+ if( nDiff > 0 )
+ {
+ if( nAdjust == SDRTEXTVERTADJUST_CENTER )
+ {
+ if( aRectFnSet.IsVertL2R() )
+ aNewContentPos.setX(aNewContentPos.getX() + nDiff/2);
+ else if( aRectFnSet.IsVert() )
+ aNewContentPos.setX(aNewContentPos.getX() - nDiff/2);
+ else
+ aNewContentPos.setY(aNewContentPos.getY() + nDiff/2);
+ }
+ else if( nAdjust == SDRTEXTVERTADJUST_BOTTOM )
+ {
+ if( aRectFnSet.IsVertL2R() )
+ aNewContentPos.setX(aNewContentPos.getX() + nDiff);
+ else if( aRectFnSet.IsVert() )
+ aNewContentPos.setX(aNewContentPos.getX() - nDiff);
+ else
+ aNewContentPos.setY(aNewContentPos.getY() + nDiff);
+ }
+ }
+ }
+ if( aNewContentPos != ContentPos() )
+ {
+ ContentPos() = aNewContentPos;
+ for( SwFrame *pFrame = Lower(); pFrame; pFrame = pFrame->GetNext())
+ {
+ pFrame->InvalidatePos();
+ }
+ }
+
+}
+
+void SwFlyFrame::InvalidateContentPos()
+{
+ m_bValidContentPos = false;
+ Invalidate_();
+}
+
+void SwFlyFrame::SelectionHasChanged(SwFEShell* pShell)
+{
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >(pShell);
+ if (pWrtSh == nullptr)
+ return;
+
+ UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
+}
+
+bool SwFlyFrame::IsShowUnfloatButton(SwWrtShell* pWrtSh) const
+{
+ if (pWrtSh == nullptr)
+ return false;
+
+ // In read only mode we don't allow unfloat operation
+ if (pWrtSh->GetViewOptions()->IsReadonly())
+ return false;
+
+ const SdrObject *pObj = GetFrameFormat().FindRealSdrObject();
+ if (pObj == nullptr)
+ return false;
+
+ // SwFlyFrame itself can mean images, ole objects, etc, but we interested in actual text frames
+ if (SwFEShell::GetObjCntType(*pObj) != OBJCNT_FLY)
+ return false;
+
+ // We show the button only for the selected text frame
+ SwDrawView *pView = pWrtSh->Imp()->GetDrawView();
+ if (pView == nullptr)
+ return false;
+
+ // Fly frame can be selected only alone
+ if (pView->GetMarkedObjectList().GetMarkCount() != 1)
+ return false;
+
+ if(!pView->IsObjMarked(pObj))
+ return false;
+
+ // A frame is a floating table if there is only one table (and maybe some whitespaces) inside it
+ int nTableCount = 0;
+ const SwFrame* pLower = GetLower();
+ const SwTabFrame* pTable = nullptr;
+ while (pLower)
+ {
+ if (pLower->IsTabFrame())
+ {
+ pTable = static_cast<const SwTabFrame*>(pLower);
+ ++nTableCount;
+ if (nTableCount > 1 || pTable == nullptr)
+ return false;
+ }
+
+ if (pLower->IsTextFrame())
+ {
+ const SwTextFrame* pTextFrame = static_cast<const SwTextFrame*>(pLower);
+ if (!pTextFrame->GetText().trim().isEmpty())
+ return false;
+ }
+ pLower = pLower->GetNext();
+ }
+
+ if (nTableCount != 1 || pTable == nullptr)
+ return false;
+
+ // Show the unfold button only for multipage tables
+ const SwBodyFrame *pBody = GetAnchorFrame()->FindBodyFrame();
+ if (pBody == nullptr)
+ return false;
+
+ tools::Long nBodyHeight = pBody->getFrameArea().Height();
+ tools::Long nTableHeight = pTable->getFrameArea().Height();
+ tools::Long nFrameOffset = std::abs(GetAnchorFrame()->getFrameArea().Top() - pBody->getFrameArea().Top());
+
+ return nBodyHeight < nTableHeight + nFrameOffset;
+}
+
+void SwFlyFrame::ActiveUnfloatButton(SwWrtShell* pWrtSh)
+{
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
+ SwFrameControlPtr pControl = rMngr.GetControl(FrameControlType::FloatingTable, this);
+ if (pControl && pControl->GetWindow())
+ {
+ pControl->GetWindow()->MouseButtonDown(MouseEvent());
+ }
+}
+
+void SwFlyFrame::UpdateUnfloatButton(SwWrtShell* pWrtSh, bool bShow) const
+{
+ if (pWrtSh == nullptr)
+ return;
+
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
+ Point aTopRightPixel = rEditWin.LogicToPixel( getFrameArea().TopRight() );
+ rMngr.SetUnfloatTableButton(this, bShow, aTopRightPixel);
+}
+
+SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst )
+{
+ SwRectFnSet aRectFnSet(this);
+ if ( Lower() && !IsColLocked() && !HasFixSize() )
+ {
+ SwTwips nSize = aRectFnSet.GetHeight(getFrameArea());
+ if( nSize > 0 && nDist > ( LONG_MAX - nSize ) )
+ nDist = LONG_MAX - nSize;
+
+ if ( nDist <= 0 )
+ return 0;
+
+ if ( Lower()->IsColumnFrame() )
+ { // If it's a Column Frame, the Format takes control of the
+ // resizing (due to the adjustment).
+ if ( !bTst )
+ {
+ // #i28701# - unlock position of Writer fly frame
+ UnlockPosition();
+ InvalidatePos_();
+ InvalidateSize();
+ }
+ return 0;
+ }
+
+ if ( !bTst )
+ {
+ const SwRect aOld( GetObjRectWithSpaces() );
+ InvalidateSize_();
+ const bool bOldLock = m_bLocked;
+ Unlock();
+ if ( IsFlyFreeFrame() )
+ {
+ // #i37068# - no format of position here
+ // and prevent move in method <CheckClip(..)>.
+ // This is needed to prevent layout loop caused by nested
+ // Writer fly frames - inner Writer fly frames format its
+ // anchor, which grows/shrinks the outer Writer fly frame.
+ // Note: position will be invalidated below.
+ setFrameAreaPositionValid(true);
+
+ // #i55416#
+ // Suppress format of width for autowidth frame, because the
+ // format of the width would call <SwTextFrame::CalcFitToContent()>
+ // for the lower frame, which initiated this grow.
+ const bool bOldFormatHeightOnly = m_bFormatHeightOnly;
+ const SwFormatFrameSize& rFrameSz = GetFormat()->GetFrameSize();
+ if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
+ {
+ m_bFormatHeightOnly = true;
+ }
+ SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if (pSh)
+ {
+ static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( true );
+ static_cast<SwFlyFreeFrame*>(this)->SwFlyFreeFrame::MakeAll(pSh->GetOut());
+ static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( false );
+ }
+ // #i55416#
+ if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
+ {
+ m_bFormatHeightOnly = bOldFormatHeightOnly;
+ }
+ }
+ else
+ MakeAll(getRootFrame()->GetCurrShell()->GetOut());
+ InvalidateSize_();
+ InvalidatePos();
+ if ( bOldLock )
+ Lock();
+ const SwRect aNew( GetObjRectWithSpaces() );
+ if ( aOld != aNew )
+ ::Notify( this, FindPageFrame(), aOld );
+ return aRectFnSet.GetHeight(aNew)-aRectFnSet.GetHeight(aOld);
+ }
+ return nDist;
+ }
+ return 0;
+}
+
+SwTwips SwFlyFrame::Shrink_( SwTwips nDist, bool bTst )
+{
+ if( Lower() && !IsColLocked() && !HasFixSize() )
+ {
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
+ if ( nDist > nHeight )
+ nDist = nHeight;
+
+ SwTwips nVal = nDist;
+ if ( IsMinHeight() )
+ {
+ const SwFormatFrameSize& rFormatSize = GetFormat()->GetFrameSize();
+ SwTwips nFormatHeight = aRectFnSet.IsVert() ? rFormatSize.GetWidth() : rFormatSize.GetHeight();
+
+ nVal = std::min( nDist, nHeight - nFormatHeight );
+ }
+
+ if ( nVal <= 0 )
+ return 0;
+
+ if ( Lower()->IsColumnFrame() )
+ { // If it's a Column Frame, the Format takes control of the
+ // resizing (due to the adjustment).
+ if ( !bTst )
+ {
+ SwRect aOld( GetObjRectWithSpaces() );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nHeight - nVal );
+ }
+
+ // #i68520#
+ if ( nHeight - nVal != 0 )
+ {
+ InvalidateObjRectWithSpaces();
+ }
+
+ nHeight = aRectFnSet.GetHeight(getFramePrintArea());
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nHeight - nVal );
+ }
+
+ InvalidatePos_();
+ InvalidateSize();
+ ::Notify( this, FindPageFrame(), aOld );
+ NotifyDrawObj();
+ if ( GetAnchorFrame()->IsInFly() )
+ AnchorFrame()->FindFlyFrame()->Shrink( nDist, bTst );
+ }
+ return 0;
+ }
+
+ if ( !bTst )
+ {
+ const SwRect aOld( GetObjRectWithSpaces() );
+ InvalidateSize_();
+ const bool bOldLocked = m_bLocked;
+ Unlock();
+ if ( IsFlyFreeFrame() )
+ {
+ // #i37068# - no format of position here
+ // and prevent move in method <CheckClip(..)>.
+ // This is needed to prevent layout loop caused by nested
+ // Writer fly frames - inner Writer fly frames format its
+ // anchor, which grows/shrinks the outer Writer fly frame.
+ // Note: position will be invalidated below.
+ setFrameAreaPositionValid(true);
+
+ // #i55416#
+ // Suppress format of width for autowidth frame, because the
+ // format of the width would call <SwTextFrame::CalcFitToContent()>
+ // for the lower frame, which initiated this shrink.
+ const bool bOldFormatHeightOnly = m_bFormatHeightOnly;
+ const SwFormatFrameSize& rFrameSz = GetFormat()->GetFrameSize();
+ if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
+ {
+ m_bFormatHeightOnly = true;
+ }
+ static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( true );
+ static_cast<SwFlyFreeFrame*>(this)->SwFlyFreeFrame::MakeAll(getRootFrame()->GetCurrShell()->GetOut());
+ static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( false );
+ // #i55416#
+ if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
+ {
+ m_bFormatHeightOnly = bOldFormatHeightOnly;
+ }
+ }
+ else
+ MakeAll(getRootFrame()->GetCurrShell()->GetOut());
+ InvalidateSize_();
+ InvalidatePos();
+ if ( bOldLocked )
+ Lock();
+ const SwRect aNew( GetObjRectWithSpaces() );
+ if ( aOld != aNew )
+ {
+ ::Notify( this, FindPageFrame(), aOld );
+ if ( GetAnchorFrame()->IsInFly() )
+ AnchorFrame()->FindFlyFrame()->Shrink( nDist, bTst );
+ }
+ return aRectFnSet.GetHeight(aOld) -
+ aRectFnSet.GetHeight(aNew);
+ }
+ return nVal;
+ }
+ return 0;
+}
+
+Size SwFlyFrame::ChgSize( const Size& aNewSize )
+{
+ // #i53298#
+ // If the fly frame anchored at-paragraph or at-character contains an OLE
+ // object, assure that the new size fits into the current clipping area
+ // of the fly frame
+ Size aAdjustedNewSize( aNewSize );
+ {
+ if ( dynamic_cast<SwFlyAtContentFrame*>(this) &&
+ Lower() && dynamic_cast<SwNoTextFrame*>(Lower()) &&
+ static_cast<SwNoTextFrame*>(Lower())->GetNode()->GetOLENode() )
+ {
+ SwRect aClipRect;
+ ::CalcClipRect( GetVirtDrawObj(), aClipRect, false );
+ if ( aAdjustedNewSize.Width() > aClipRect.Width() )
+ {
+ aAdjustedNewSize.setWidth( aClipRect.Width() );
+ }
+ if ( aAdjustedNewSize.Height() > aClipRect.Height() )
+ {
+ aAdjustedNewSize.setWidth( aClipRect.Height() );
+ }
+ }
+ }
+
+ if ( aAdjustedNewSize != getFrameArea().SSize() )
+ {
+ SwFrameFormat *pFormat = GetFormat();
+ SwFormatFrameSize aSz( pFormat->GetFrameSize() );
+ aSz.SetWidth( aAdjustedNewSize.Width() );
+ aSz.SetHeight( aAdjustedNewSize.Height() );
+ // go via the Doc for UNDO
+ pFormat->GetDoc()->SetAttr( aSz, *pFormat );
+ return aSz.GetSize();
+ }
+ else
+ return getFrameArea().SSize();
+}
+
+bool SwFlyFrame::IsLowerOf( const SwLayoutFrame* pUpperFrame ) const
+{
+ OSL_ENSURE( GetAnchorFrame(), "8-( Fly is lost in Space." );
+ const SwFrame* pFrame = GetAnchorFrame();
+ do
+ {
+ if ( pFrame == pUpperFrame )
+ return true;
+ pFrame = pFrame->IsFlyFrame()
+ ? static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame()
+ : pFrame->GetUpper();
+ } while ( pFrame );
+ return false;
+}
+
+void SwFlyFrame::Cut()
+{
+}
+
+void SwFrame::AppendFly( SwFlyFrame *pNew )
+{
+ if (!m_pDrawObjs)
+ {
+ m_pDrawObjs.reset(new SwSortedObjs());
+ }
+ m_pDrawObjs->Insert( *pNew );
+ pNew->ChgAnchorFrame( this );
+
+ // Register at the page
+ // If there's none present, register via SwPageFrame::PreparePage
+ SwPageFrame* pPage = FindPageFrame();
+ if ( pPage != nullptr )
+ {
+ pPage->AppendFlyToPage( pNew );
+ }
+}
+
+void SwFrame::RemoveFly( SwFlyFrame *pToRemove )
+{
+ // Deregister from the page
+ // Could already have happened, if the page was already destructed
+ SwPageFrame *pPage = pToRemove->FindPageFrame();
+ if ( pPage && pPage->GetSortedObjs() )
+ {
+ pPage->RemoveFlyFromPage( pToRemove );
+ }
+ // #i73201#
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ else
+ {
+ if ( pToRemove->IsAccessibleFrame() &&
+ pToRemove->GetFormat() &&
+ !pToRemove->IsFlyInContentFrame() )
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
+ {
+ SwViewShell *pVSh = pRootFrame->GetCurrShell();
+ if( pVSh && pVSh->Imp() )
+ {
+ pVSh->Imp()->DisposeAccessibleFrame( pToRemove );
+ }
+ }
+ }
+ }
+#endif
+
+ m_pDrawObjs->Remove(*pToRemove);
+ if (!m_pDrawObjs->size())
+ {
+ m_pDrawObjs.reset();
+ }
+
+ pToRemove->ChgAnchorFrame( nullptr );
+
+ if ( !pToRemove->IsFlyInContentFrame() && GetUpper() && IsInTab() )//MA_FLY_HEIGHT
+ GetUpper()->InvalidateSize();
+}
+
+void SwFrame::AppendDrawObj( SwAnchoredObject& _rNewObj )
+{
+ assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
+
+ if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rNewObj) == nullptr )
+ {
+ OSL_FAIL( "SwFrame::AppendDrawObj(..) - anchored object of unexpected type -> object not appended" );
+ return;
+ }
+
+ if ( dynamic_cast<const SwDrawVirtObj*>(_rNewObj.GetDrawObj()) == nullptr &&
+ _rNewObj.GetAnchorFrame() && _rNewObj.GetAnchorFrame() != this )
+ {
+ assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
+ // perform disconnect from layout, if 'master' drawing object is appended
+ // to a new frame.
+ static_cast<SwDrawContact*>(::GetUserCall( _rNewObj.GetDrawObj() ))->
+ DisconnectFromLayout( false );
+ assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
+ }
+
+ if ( _rNewObj.GetAnchorFrame() != this )
+ {
+ if (!m_pDrawObjs)
+ {
+ m_pDrawObjs.reset(new SwSortedObjs());
+ }
+ m_pDrawObjs->Insert(_rNewObj);
+ _rNewObj.ChgAnchorFrame( this );
+ }
+
+ // #i113730#
+ // Assure the control objects and group objects containing controls are on the control layer
+ if ( ::CheckControlLayer( _rNewObj.DrawObj() ) )
+ {
+ const IDocumentDrawModelAccess& rIDDMA = getIDocumentDrawModelAccess();
+ const SdrLayerID aCurrentLayer(_rNewObj.DrawObj()->GetLayer());
+ const SdrLayerID aControlLayerID(rIDDMA.GetControlsId());
+ const SdrLayerID aInvisibleControlLayerID(rIDDMA.GetInvisibleControlsId());
+
+ if(aCurrentLayer != aControlLayerID && aCurrentLayer != aInvisibleControlLayerID)
+ {
+ if ( aCurrentLayer == rIDDMA.GetInvisibleHellId() ||
+ aCurrentLayer == rIDDMA.GetInvisibleHeavenId() )
+ {
+ _rNewObj.DrawObj()->SetLayer(aInvisibleControlLayerID);
+ }
+ else
+ {
+ _rNewObj.DrawObj()->SetLayer(aControlLayerID);
+ }
+ //The layer is part of the key used to sort the obj, so update
+ //its position if the layer changed.
+ m_pDrawObjs->Update(_rNewObj);
+ }
+ }
+
+ // no direct positioning needed, but invalidate the drawing object position
+ _rNewObj.InvalidateObjPos();
+
+ // register at page frame
+ SwPageFrame* pPage = FindPageFrame();
+ if ( pPage )
+ {
+ pPage->AppendDrawObjToPage( _rNewObj );
+ }
+
+ // Notify accessible layout.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if( pSh )
+ {
+ SwRootFrame* pLayout = getRootFrame();
+ if( pLayout && pLayout->IsAnyShellAccessible() )
+ {
+ pSh->Imp()->AddAccessibleObj( _rNewObj.GetDrawObj() );
+ }
+ }
+#endif
+
+ assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
+}
+
+void SwFrame::RemoveDrawObj( SwAnchoredObject& _rToRemoveObj )
+{
+ // Notify accessible layout.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if( pSh )
+ {
+ SwRootFrame* pLayout = getRootFrame();
+ if (pLayout && pLayout->IsAnyShellAccessible())
+ pSh->Imp()->DisposeAccessibleObj(_rToRemoveObj.GetDrawObj(), false);
+ }
+#endif
+
+ // deregister from page frame
+ SwPageFrame* pPage = _rToRemoveObj.GetPageFrame();
+ if ( pPage && pPage->GetSortedObjs() )
+ pPage->RemoveDrawObjFromPage( _rToRemoveObj );
+
+ m_pDrawObjs->Remove(_rToRemoveObj);
+ if (!m_pDrawObjs->size())
+ {
+ m_pDrawObjs.reset();
+ }
+ _rToRemoveObj.ChgAnchorFrame( nullptr );
+
+ assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
+}
+
+void SwFrame::InvalidateObjs( const bool _bNoInvaOfAsCharAnchoredObjs )
+{
+ if ( !GetDrawObjs() )
+ return;
+
+ // #i26945# - determine page the frame is on,
+ // in order to check, if anchored object is registered at the same
+ // page.
+ const SwPageFrame* pPageFrame = FindPageFrame();
+ // #i28701# - re-factoring
+ for (SwAnchoredObject* pAnchoredObj : *GetDrawObjs())
+ {
+ if ( _bNoInvaOfAsCharAnchoredObjs &&
+ (pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ == RndStdIds::FLY_AS_CHAR) )
+ {
+ continue;
+ }
+ // #i26945# - no invalidation, if anchored object
+ // isn't registered at the same page and instead is registered at
+ // the page, where its anchor character text frame is on.
+ if ( pAnchoredObj->GetPageFrame() &&
+ pAnchoredObj->GetPageFrame() != pPageFrame )
+ {
+ SwTextFrame* pAnchorCharFrame = pAnchoredObj->FindAnchorCharFrame();
+ if ( pAnchorCharFrame &&
+ pAnchoredObj->GetPageFrame() == pAnchorCharFrame->FindPageFrame() )
+ {
+ continue;
+ }
+ // #115759# - unlock its position, if anchored
+ // object isn't registered at the page, where its anchor
+ // character text frame is on, respectively if it has no
+ // anchor character text frame.
+ else
+ {
+ pAnchoredObj->UnlockPosition();
+ }
+ }
+ // #i51474# - reset flag, that anchored object
+ // has cleared environment, and unlock its position, if the anchored
+ // object is registered at the same page as the anchor frame is on.
+ if ( pAnchoredObj->ClearedEnvironment() &&
+ pAnchoredObj->GetPageFrame() &&
+ pAnchoredObj->GetPageFrame() == pPageFrame )
+ {
+ pAnchoredObj->UnlockPosition();
+ pAnchoredObj->SetClearedEnvironment( false );
+ }
+ // distinguish between writer fly frames and drawing objects
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ pFly->Invalidate_();
+ pFly->InvalidatePos_();
+ }
+ else
+ {
+ pAnchoredObj->InvalidateObjPos();
+ }
+ } // end of loop on objects, which are connected to the frame
+}
+
+// #i26945# - correct check, if anchored object is a lower
+// of the layout frame. E.g., anchor character text frame can be a follow text
+// frame.
+// #i44016# - add parameter <_bUnlockPosOfObjs> to
+// force an unlockposition call for the lower objects.
+void SwLayoutFrame::NotifyLowerObjs( const bool _bUnlockPosOfObjs )
+{
+ // invalidate lower floating screen objects
+ SwPageFrame* pPageFrame = FindPageFrame();
+ if ( !(pPageFrame && pPageFrame->GetSortedObjs()) )
+ return;
+
+ SwSortedObjs& rObjs = *(pPageFrame->GetSortedObjs());
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ // #i26945# - check if anchored object is a lower
+ // of the layout frame is changed to check, if its anchor frame
+ // is a lower of the layout frame.
+ // Determine the anchor frame - usually it's the anchor frame,
+ // for at-character/as-character anchored objects the anchor character
+ // text frame is taken.
+ const SwFrame* pAnchorFrame = pObj->GetAnchorFrameContainingAnchPos();
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->getFrameArea().Left() == FAR_AWAY )
+ continue;
+
+ if ( pFly->IsAnLower( this ) )
+ continue;
+
+ // #i26945# - use <pAnchorFrame> to check, if
+ // fly frame is lower of layout frame resp. if fly frame is
+ // at a different page registered as its anchor frame is on.
+ const bool bLow = IsAnLower( pAnchorFrame );
+ if ( bLow || pAnchorFrame->FindPageFrame() != pPageFrame )
+ {
+ pFly->Invalidate_( pPageFrame );
+ if ( !bLow || pFly->IsFlyAtContentFrame() )
+ {
+ // #i44016#
+ if ( _bUnlockPosOfObjs )
+ {
+ pFly->UnlockPosition();
+ }
+ pFly->InvalidatePos_();
+ }
+ else
+ pFly->InvalidatePrt_();
+ }
+ }
+ else
+ {
+ assert( dynamic_cast<const SwAnchoredDrawObject*>( pObj) &&
+ "<SwLayoutFrame::NotifyFlys() - anchored object of unexpected type" );
+ // #i26945# - use <pAnchorFrame> to check, if
+ // fly frame is lower of layout frame resp. if fly frame is
+ // at a different page registered as its anchor frame is on.
+ if ( IsAnLower( pAnchorFrame ) ||
+ pAnchorFrame->FindPageFrame() != pPageFrame )
+ {
+ // #i44016#
+ if ( _bUnlockPosOfObjs )
+ {
+ pObj->UnlockPosition();
+ }
+ pObj->InvalidateObjPos();
+ }
+ }
+ }
+}
+
+void SwFlyFrame::NotifyDrawObj()
+{
+ SwVirtFlyDrawObj* pObj = GetVirtDrawObj();
+ pObj->SetRect();
+ pObj->SetBoundAndSnapRectsDirty();
+ pObj->SetChanged();
+ pObj->BroadcastObjectChange();
+
+ if ( GetFormat()->GetSurround().IsContour() )
+ {
+ ClrContourCache( pObj );
+ }
+ else if(IsFlyFreeFrame() && static_cast< const SwFlyFreeFrame* >(this)->supportsAutoContour())
+ {
+ // RotateFlyFrame3: Also need to clear when changes happen
+ // Caution: isTransformableSwFrame is already reset when resetting rotation, so
+ // *additionally* reset in SwFlyFreeFrame::MakeAll when no more rotation
+ ClrContourCache( pObj );
+ }
+}
+
+Size SwFlyFrame::CalcRel( const SwFormatFrameSize &rSz ) const
+{
+ Size aRet( rSz.GetSize() );
+
+ const SwFrame *pRel = IsFlyLayFrame() ? GetAnchorFrame() : GetAnchorFrame()->GetUpper();
+ if( pRel ) // LAYER_IMPL
+ {
+ tools::Long nRelWidth = LONG_MAX, nRelHeight = LONG_MAX;
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( ( pRel->IsBodyFrame() || pRel->IsPageFrame() ) &&
+ pSh && pSh->GetViewOptions()->getBrowseMode() &&
+ pSh->VisArea().HasArea() )
+ {
+ nRelWidth = pSh->GetBrowseWidth();
+ nRelHeight = pSh->VisArea().Height();
+ Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
+ nRelWidth = std::min( nRelWidth, pRel->getFramePrintArea().Width() );
+ nRelHeight -= 2*aBorder.Height();
+ nRelHeight = std::min( nRelHeight, pRel->getFramePrintArea().Height() );
+ }
+
+ // At the moment only the "== PAGE_FRAME" and "!= PAGE_FRAME" cases are handled.
+ // When size is a relative to page size, ignore size of SwBodyFrame.
+ if (rSz.GetWidthPercentRelation() != text::RelOrientation::PAGE_FRAME)
+ nRelWidth = std::min( nRelWidth, pRel->getFramePrintArea().Width() );
+ else if ( pRel->IsPageFrame() )
+ nRelWidth = std::min( nRelWidth, pRel->getFrameArea().Width() );
+
+ if (rSz.GetHeightPercentRelation() != text::RelOrientation::PAGE_FRAME)
+ nRelHeight = std::min( nRelHeight, pRel->getFramePrintArea().Height() );
+ else if ( pRel->IsPageFrame() )
+ nRelHeight = std::min( nRelHeight, pRel->getFrameArea().Height() );
+
+ if( !pRel->IsPageFrame() )
+ {
+ const SwPageFrame* pPage = FindPageFrame();
+ if( pPage )
+ {
+ if (rSz.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME)
+ // Ignore margins of pPage.
+ nRelWidth = std::min( nRelWidth, pPage->getFrameArea().Width() );
+ else
+ nRelWidth = std::min( nRelWidth, pPage->getFramePrintArea().Width() );
+ if (rSz.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME)
+ // Ignore margins of pPage.
+ nRelHeight = std::min( nRelHeight, pPage->getFrameArea().Height() );
+ else
+ nRelHeight = std::min( nRelHeight, pPage->getFramePrintArea().Height() );
+ }
+ }
+
+ if ( rSz.GetWidthPercent() && rSz.GetWidthPercent() != SwFormatFrameSize::SYNCED )
+ aRet.setWidth( nRelWidth * rSz.GetWidthPercent() / 100 );
+ if ( rSz.GetHeightPercent() && rSz.GetHeightPercent() != SwFormatFrameSize::SYNCED )
+ aRet.setHeight( nRelHeight * rSz.GetHeightPercent() / 100 );
+
+ if ( rSz.GetHeight() && rSz.GetWidthPercent() == SwFormatFrameSize::SYNCED )
+ {
+ aRet.setWidth( aRet.Width() * ( aRet.Height()) );
+ aRet.setWidth( aRet.Width() / ( rSz.GetHeight()) );
+ }
+ else if ( rSz.GetWidth() && rSz.GetHeightPercent() == SwFormatFrameSize::SYNCED )
+ {
+ aRet.setHeight( aRet.Height() * ( aRet.Width()) );
+ aRet.setHeight( aRet.Height() / ( rSz.GetWidth()) );
+ }
+ }
+ return aRet;
+}
+
+static SwTwips lcl_CalcAutoWidth( const SwLayoutFrame& rFrame )
+{
+ SwTwips nRet = 0;
+ SwTwips nMin = 0;
+ const SwFrame* pFrame = rFrame.Lower();
+
+ // No autowidth defined for columned frames
+ if ( !pFrame || pFrame->IsColumnFrame() )
+ return nRet;
+
+ int nParagraphCount = 0;
+ while ( pFrame )
+ {
+ nParagraphCount++;
+ if ( pFrame->IsSctFrame() )
+ {
+ nMin = lcl_CalcAutoWidth( *static_cast<const SwSectionFrame*>(pFrame) );
+ }
+ if ( pFrame->IsTextFrame() )
+ {
+ nMin = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pFrame))->CalcFitToContent();
+ const SvxLRSpaceItem &rSpace =
+ static_cast<const SwTextFrame*>(pFrame)->GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace();
+ if (!static_cast<const SwTextFrame*>(pFrame)->IsLocked())
+ nMin += rSpace.GetRight() + rSpace.GetTextLeft() + rSpace.GetTextFirstLineOffset();
+ }
+ else if ( pFrame->IsTabFrame() )
+ {
+ const SwFormatFrameSize& rTableFormatSz = static_cast<const SwTabFrame*>(pFrame)->GetTable()->GetFrameFormat()->GetFrameSize();
+ if ( USHRT_MAX == rTableFormatSz.GetSize().Width() ||
+ text::HoriOrientation::NONE == static_cast<const SwTabFrame*>(pFrame)->GetFormat()->GetHoriOrient().GetHoriOrient() )
+ {
+ const SwPageFrame* pPage = rFrame.FindPageFrame();
+ // auto width table
+ nMin = pFrame->GetUpper()->IsVertical() ?
+ pPage->getFramePrintArea().Height() :
+ pPage->getFramePrintArea().Width();
+ }
+ else
+ {
+ nMin = rTableFormatSz.GetSize().Width();
+ }
+ }
+
+ if ( nMin > nRet )
+ nRet = nMin;
+
+ pFrame = pFrame->GetNext();
+ }
+
+ // tdf#124423 In Microsoft compatibility mode: widen the frame to max (PrintArea of the frame it anchored to) if it contains at least 2 paragraphs,
+ // or 1 paragraph wider than its parent area.
+ if (rFrame.GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA))
+ {
+ const SwFrame* pFrameRect = rFrame.IsFlyFrame() ? static_cast<const SwFlyFrame*>(&rFrame)->GetAnchorFrame() : rFrame.Lower()->FindPageFrame();
+ SwTwips nParentWidth = rFrame.IsVertical() ? pFrameRect->getFramePrintArea().Height() : pFrameRect->getFramePrintArea().Width();
+ if (nParagraphCount > 1 || nRet > nParentWidth)
+ {
+ return nParentWidth;
+ }
+ }
+
+ return nRet;
+}
+
+/// #i13147# - If called for paint and the <SwNoTextFrame> contains
+/// a graphic, load of intrinsic graphic has to be avoided.
+bool SwFlyFrame::GetContour( tools::PolyPolygon& rContour,
+ const bool _bForPaint ) const
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ bool bRet = false;
+ const bool bIsCandidate(Lower() && Lower()->IsNoTextFrame());
+
+ if(bIsCandidate)
+ {
+ if(GetFormat()->GetSurround().IsContour())
+ {
+ SwNoTextNode *pNd = const_cast<SwNoTextNode*>(static_cast<const SwNoTextNode*>(static_cast<const SwNoTextFrame*>(Lower())->GetNode()));
+ // #i13147# - determine <GraphicObject> instead of <Graphic>
+ // in order to avoid load of graphic, if <SwNoTextNode> contains a graphic
+ // node and method is called for paint.
+ std::unique_ptr<GraphicObject> xTmpGrfObj;
+ const GraphicObject* pGrfObj = nullptr;
+ const SwGrfNode* pGrfNd = pNd->GetGrfNode();
+ if ( pGrfNd && _bForPaint )
+ {
+ pGrfObj = &(pGrfNd->GetGrfObj());
+ }
+ else
+ {
+ xTmpGrfObj.reset(new GraphicObject(pNd->GetGraphic()));
+ pGrfObj = xTmpGrfObj.get();
+ }
+ assert(pGrfObj && "SwFlyFrame::GetContour() - No Graphic/GraphicObject found at <SwNoTextNode>.");
+ if (pGrfObj->GetType() != GraphicType::NONE)
+ {
+ if( !pNd->HasContour() )
+ {
+ //#i13147# - no <CreateContour> for a graphic
+ // during paint. Thus, return (value of <bRet> should be <false>).
+ if ( pGrfNd && _bForPaint )
+ {
+ OSL_FAIL( "SwFlyFrame::GetContour() - No Contour found at <SwNoTextNode> during paint." );
+ return bRet;
+ }
+ pNd->CreateContour();
+ }
+ pNd->GetContour( rContour );
+ // The Node holds the Polygon matching the original size of the graphic
+ // We need to include the scaling here
+ SwRect aClip;
+ SwRect aOrig;
+ Lower()->Calc(pRenderContext);
+ static_cast<const SwNoTextFrame*>(Lower())->GetGrfArea( aClip, &aOrig );
+ // #i13147# - copy method code <SvxContourDlg::ScaleContour(..)>
+ // in order to avoid that graphic has to be loaded for contour scale.
+ //SvxContourDlg::ScaleContour( rContour, aGrf, MapUnit::MapTwip, aOrig.SSize() );
+ {
+ OutputDevice* pOutDev = Application::GetDefaultDevice();
+ const MapMode aDispMap( MapUnit::MapTwip );
+ const MapMode aGrfMap( pGrfObj->GetPrefMapMode() );
+ const Size aGrfSize( pGrfObj->GetPrefSize() );
+ Size aOrgSize;
+ Point aNewPoint;
+ bool bPixelMap = aGrfMap.GetMapUnit() == MapUnit::MapPixel;
+
+ if ( bPixelMap )
+ aOrgSize = pOutDev->PixelToLogic( aGrfSize, aDispMap );
+ else
+ aOrgSize = OutputDevice::LogicToLogic( aGrfSize, aGrfMap, aDispMap );
+
+ if ( aOrgSize.Width() && aOrgSize.Height() )
+ {
+ double fScaleX = static_cast<double>(aOrig.Width()) / aOrgSize.Width();
+ double fScaleY = static_cast<double>(aOrig.Height()) / aOrgSize.Height();
+
+ for ( sal_uInt16 j = 0, nPolyCount = rContour.Count(); j < nPolyCount; j++ )
+ {
+ tools::Polygon& rPoly = rContour[ j ];
+
+ for ( sal_uInt16 i = 0, nCount = rPoly.GetSize(); i < nCount; i++ )
+ {
+ if ( bPixelMap )
+ aNewPoint = pOutDev->PixelToLogic( rPoly[ i ], aDispMap );
+ else
+ aNewPoint = OutputDevice::LogicToLogic( rPoly[ i ], aGrfMap, aDispMap );
+
+ rPoly[ i ] = Point( FRound( aNewPoint.getX() * fScaleX ), FRound( aNewPoint.getY() * fScaleY ) );
+ }
+ }
+ }
+ }
+ // destroy created <GraphicObject>.
+ xTmpGrfObj.reset();
+ rContour.Move( aOrig.Left(), aOrig.Top() );
+ if( !aClip.Width() )
+ aClip.Width( 1 );
+ if( !aClip.Height() )
+ aClip.Height( 1 );
+ rContour.Clip( aClip.SVRect() );
+ rContour.Optimize(PolyOptimizeFlags::CLOSE);
+ bRet = true;
+ }
+ }
+ else if (IsFlyFreeFrame())
+ {
+ const SwFlyFreeFrame* pSwFlyFreeFrame(static_cast< const SwFlyFreeFrame* >(this));
+
+ if(nullptr != pSwFlyFreeFrame &&
+ pSwFlyFreeFrame->supportsAutoContour() &&
+ // isTransformableSwFrame already used in supportsAutoContour(), but
+ // better check twice when it may get changed there...
+ pSwFlyFreeFrame->isTransformableSwFrame())
+ {
+ // RotateFlyFrame: use untransformed SwFrame to allow text floating around.
+ // Will be transformed below
+ const TransformableSwFrame* pTransformableSwFrame(pSwFlyFreeFrame->getTransformableSwFrame());
+ const SwRect aFrameArea(pTransformableSwFrame->getUntransformedFrameArea());
+ rContour = tools::PolyPolygon(tools::Polygon(aFrameArea.SVRect()));
+ bRet = (0 != rContour.Count());
+ }
+ }
+
+ if(bRet && 0 != rContour.Count())
+ {
+ if (IsFlyFreeFrame() &&
+ static_cast< const SwFlyFreeFrame* >(this)->isTransformableSwFrame())
+ {
+ // Need to adapt contour to transformation
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getFrameAreaTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ if(!basegfx::fTools::equalZero(fRotate))
+ {
+ basegfx::B2DPolyPolygon aSource(rContour.getB2DPolyPolygon());
+ const basegfx::B2DPoint aCenter(getFrameAreaTransformation() * basegfx::B2DPoint(0.5, 0.5));
+ const basegfx::B2DHomMatrix aRotateAroundCenter(
+ basegfx::utils::createRotateAroundPoint(
+ aCenter.getX(),
+ aCenter.getY(),
+ fRotate));
+ aSource.transform(aRotateAroundCenter);
+ rContour = tools::PolyPolygon(aSource);
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+
+const SwVirtFlyDrawObj* SwFlyFrame::GetVirtDrawObj() const
+{
+ return static_cast<const SwVirtFlyDrawObj*>(GetDrawObj());
+}
+SwVirtFlyDrawObj* SwFlyFrame::GetVirtDrawObj()
+{
+ return static_cast<SwVirtFlyDrawObj*>(DrawObj());
+}
+
+// implementation of pure virtual method declared in
+// base class <SwAnchoredObject>
+
+void SwFlyFrame::InvalidateObjPos()
+{
+ InvalidatePos();
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+}
+
+SwFrameFormat& SwFlyFrame::GetFrameFormat()
+{
+ OSL_ENSURE( GetFormat(),
+ "<SwFlyFrame::GetFrameFormat()> - missing frame format -> crash." );
+ return *GetFormat();
+}
+const SwFrameFormat& SwFlyFrame::GetFrameFormat() const
+{
+ OSL_ENSURE( GetFormat(),
+ "<SwFlyFrame::GetFrameFormat()> - missing frame format -> crash." );
+ return *GetFormat();
+}
+
+SwRect SwFlyFrame::GetObjRect() const
+{
+ return getFrameArea();
+}
+
+// #i70122#
+// for Writer fly frames the bounding rectangle equals the object rectangles
+SwRect SwFlyFrame::GetObjBoundRect() const
+{
+ return GetObjRect();
+}
+
+// #i68520#
+bool SwFlyFrame::SetObjTop_( const SwTwips _nTop )
+{
+ const bool bChanged( getFrameArea().Pos().getY() != _nTop );
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setY(_nTop);
+
+ return bChanged;
+}
+bool SwFlyFrame::SetObjLeft_( const SwTwips _nLeft )
+{
+ const bool bChanged( getFrameArea().Pos().getX() != _nLeft );
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(_nLeft);
+
+ return bChanged;
+}
+
+/** method to assure that anchored object is registered at the correct
+ page frame
+
+ OD 2004-07-02 #i28701#
+*/
+void SwFlyFrame::RegisterAtCorrectPage()
+{
+ // default behaviour is to do nothing.
+}
+
+void SwFlyFrame::RegisterAtPage(SwPageFrame &)
+{
+ // default behaviour is to do nothing.
+}
+
+/** method to determine, if a <MakeAll()> on the Writer fly frame is possible
+
+ OD 2004-05-11 #i28701#
+*/
+bool SwFlyFrame::IsFormatPossible() const
+{
+ return SwAnchoredObject::IsFormatPossible() &&
+ !IsLocked() && !IsColLocked();
+}
+
+void SwFlyFrame::GetAnchoredObjects( std::vector<SwAnchoredObject*>& aVector, const SwFormat& rFormat )
+{
+ SwIterator<SwFlyFrame,SwFormat> aIter( rFormat );
+ for( SwFlyFrame* pFlyFrame = aIter.First(); pFlyFrame; pFlyFrame = aIter.Next() )
+ aVector.push_back( pFlyFrame );
+}
+
+const SwFlyFrameFormat * SwFlyFrame::GetFormat() const
+{
+ return static_cast< const SwFlyFrameFormat * >( GetDep() );
+}
+
+SwFlyFrameFormat * SwFlyFrame::GetFormat()
+{
+ return static_cast< SwFlyFrameFormat * >( GetDep() );
+}
+
+void SwFlyFrame::Calc(vcl::RenderContext* pRenderContext) const
+{
+ if ( !m_bValidContentPos )
+ const_cast<SwFlyFrame*>(this)->PrepareMake(pRenderContext);
+ else
+ SwLayoutFrame::Calc(pRenderContext);
+}
+
+SwTwips SwFlyFrame::CalcContentHeight(const SwBorderAttrs *pAttrs, const SwTwips nMinHeight, const SwTwips nUL)
+{
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nHeight = 0;
+ if ( Lower() )
+ {
+ if ( Lower()->IsColumnFrame() )
+ {
+ FormatWidthCols( *pAttrs, nUL, nMinHeight );
+ nHeight = aRectFnSet.GetHeight(Lower()->getFrameArea());
+ }
+ else
+ {
+ SwFrame *pFrame = Lower();
+ while ( pFrame )
+ {
+ nHeight += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ // This TextFrame would like to be a bit larger
+ nHeight += static_cast<SwTextFrame*>(pFrame)->GetParHeight()
+ - aRectFnSet.GetHeight(pFrame->getFramePrintArea());
+ else if( pFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
+ nHeight += static_cast<SwSectionFrame*>(pFrame)->Undersize();
+ pFrame = pFrame->GetNext();
+ }
+ }
+ if ( GetDrawObjs() )
+ {
+ const size_t nCnt = GetDrawObjs()->size();
+ SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
+ SwTwips nBorder = aRectFnSet.GetHeight(getFrameArea()) -
+ aRectFnSet.GetHeight(getFramePrintArea());
+ for ( size_t i = 0; i < nCnt; ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[i];
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ // consider only Writer fly frames, which follow the text flow.
+ if ( pFly->IsFlyLayFrame() &&
+ pFly->getFrameArea().Top() != FAR_AWAY &&
+ pFly->GetFormat()->GetFollowTextFlow().GetValue() )
+ {
+ SwTwips nDist = -aRectFnSet.BottomDist( pFly->getFrameArea(), nTop );
+ if( nDist > nBorder + nHeight )
+ nHeight = nDist - nBorder;
+ }
+ }
+ }
+ }
+ }
+ return nHeight;
+}
+
+const SwFormatAnchor* SwFlyFrame::GetAnchorFromPoolItem(const SfxPoolItem& rItem)
+{
+ switch(rItem.Which())
+ {
+ case RES_ATTRSET_CHG:
+ return rItem.StaticWhichCast(RES_ATTRSET_CHG).GetChgSet()->GetItem(RES_ANCHOR, false);
+ case RES_ANCHOR:
+ return static_cast<const SwFormatAnchor*>(&rItem);
+ default:
+ return nullptr;
+ }
+}
+
+const SwFlyFrame* SwFlyFrame::DynCastFlyFrame() const
+{
+ return this;
+}
+
+SwFlyFrame* SwFlyFrame::DynCastFlyFrame()
+{
+ return this;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flycnt.cxx b/sw/source/core/layout/flycnt.cxx
new file mode 100644
index 000000000..f7611392d
--- /dev/null
+++ b/sw/source/core/layout/flycnt.cxx
@@ -0,0 +1,1522 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svx/swframetypes.hxx>
+#include <pagefrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <doc.hxx>
+#include <pam.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <frmtool.hxx>
+#include <dflyobj.hxx>
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtsrnd.hxx>
+#include <txatbase.hxx>
+
+#include <tabfrm.hxx>
+#include <flyfrms.hxx>
+#include <crstate.hxx>
+#include <sectfrm.hxx>
+
+#include <tocntntanchoredobjectposition.hxx>
+#include <sortedobjs.hxx>
+#include <layouter.hxx>
+#include "objectformattertxtfrm.hxx"
+#include <HandleAnchorNodeChg.hxx>
+#include <ndtxt.hxx>
+#include <textboxhelper.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <unoprnms.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+
+SwTwips lcl_GetTopForObjPos(const SwContentFrame* pCnt, const bool bVert, const bool bVertL2R)
+{
+ if ( bVert )
+ {
+ SwTwips aResult = pCnt->getFrameArea().Left();
+ if ( bVertL2R )
+ aResult += pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ else
+ aResult += pCnt->getFrameArea().Width() - pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ return aResult;
+ }
+ else
+ return pCnt->getFrameArea().Top() + pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+}
+
+}
+
+SwFlyAtContentFrame::SwFlyAtContentFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch ) :
+ SwFlyFreeFrame( pFormat, pSib, pAnch )
+{
+ m_bAtCnt = true;
+ m_bAutoPosition = (RndStdIds::FLY_AT_CHAR == pFormat->GetAnchor().GetAnchorId());
+}
+
+// #i28701#
+
+void SwFlyAtContentFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ {
+ SwFlyFrame::SwClientNotify(rMod, rHint);
+ return;
+ }
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const SwFormatAnchor* pAnch = pLegacy->m_pNew ? GetAnchorFromPoolItem(*pLegacy->m_pNew) : nullptr;
+ if(!pAnch)
+ {
+ SwFlyFrame::SwClientNotify(rMod, rHint);
+ return;
+ }
+ OSL_ENSURE(pAnch->GetAnchorId() == GetFormat()->GetAnchor().GetAnchorId(),
+ "Illegal change of anchor type.");
+
+ //Unregister, get hold of a new anchor and attach it
+ SwRect aOld(GetObjRectWithSpaces());
+ SwPageFrame* pOldPage = FindPageFrame();
+ const SwFrame* pOldAnchor = GetAnchorFrame();
+ SwContentFrame* pContent = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(GetAnchorFrame()));
+ AnchorFrame()->RemoveFly(this);
+
+ const bool bBodyFootnote = (pContent->IsInDocBody() || pContent->IsInFootnote());
+
+ // Search the new anchor using the NodeIdx; the relation between old
+ // and new NodeIdx determines the search direction
+ const SwNodeIndex aNewIdx(pAnch->GetContentAnchor()->nNode);
+ SwNodeIndex aOldIdx(pContent->IsTextFrame()
+ // sw_redlinehide: can pick any node here, the compare with
+ // FrameContainsNode should catch it
+ ? *static_cast<SwTextFrame *>(pContent)->GetTextNodeFirst()
+ : *static_cast<SwNoTextFrame *>(pContent)->GetNode());
+
+ //fix: depending on which index was smaller, searching in the do-while
+ //loop previously was done forward or backwards respectively. This however
+ //could lead to an infinite loop. To at least avoid the loop, searching
+ //is now done in only one direction. Getting hold of a frame from the node
+ //is still possible if the new anchor could not be found. Chances are
+ //good that this will be the correct one.
+ // consider the case that at found anchor frame candidate already a
+ // fly frame of the given fly format is registered.
+ // consider, that <pContent> is the already
+ // the new anchor frame.
+ bool bFound(FrameContainsNode(*pContent, aNewIdx.GetIndex()));
+ const bool bNext = !bFound && aOldIdx < aNewIdx;
+ while(pContent && !bFound)
+ {
+ do
+ {
+ if(bNext)
+ pContent = pContent->GetNextContentFrame();
+ else
+ pContent = pContent->GetPrevContentFrame();
+ } while(pContent &&
+ (bBodyFootnote != (pContent->IsInDocBody() || pContent->IsInFootnote())));
+ if(pContent)
+ bFound = FrameContainsNode(*pContent, aNewIdx.GetIndex());
+
+ // check, if at found anchor frame candidate already a fly frame
+ // of the given fly frame format is registered.
+ if(bFound && pContent && pContent->GetDrawObjs())
+ {
+ SwFrameFormat* pMyFlyFrameFormat(&GetFrameFormat());
+ SwSortedObjs &rObjs = *pContent->GetDrawObjs();
+ for(SwAnchoredObject* rObj : rObjs)
+ {
+ SwFlyFrame* pFlyFrame = rObj->DynCastFlyFrame();
+ if (pFlyFrame &&
+ &(pFlyFrame->GetFrameFormat()) == pMyFlyFrameFormat)
+ {
+ bFound = false;
+ break;
+ }
+ }
+ }
+ }
+ if(!pContent)
+ {
+ SwContentNode *pNode = aNewIdx.GetNode().GetContentNode();
+ std::pair<Point, bool> const tmp(pOldAnchor->getFrameArea().Pos(), false);
+ pContent = pNode->getLayoutFrame(getRootFrame(), nullptr, &tmp);
+ OSL_ENSURE(pContent, "New anchor not found");
+ }
+ //Flys are never attached to a follow, but always on the master which
+ //we are going to search now.
+ SwContentFrame* pFlow = pContent;
+ while(pFlow->IsFollow())
+ pFlow = pFlow->FindMaster();
+ pContent = pFlow;
+
+ //and *puff* it's attached...
+ pContent->AppendFly( this );
+ if(pOldPage && pOldPage != FindPageFrame())
+ NotifyBackground(pOldPage, aOld, PrepareHint::FlyFrameLeave);
+
+ //Fix(3495)
+ InvalidatePos_();
+ InvalidatePage();
+ SetNotifyBack();
+ // #i28701# - reset member <maLastCharRect> and
+ // <mnLastTopOfLine> for to-character anchored objects.
+ ClearCharRectAndTopOfLine();
+}
+
+//We need some helper classes to monitor the oscillation and a few functions
+//to not get lost.
+
+namespace {
+
+// #i3317# - re-factoring of the position stack
+class SwOszControl
+{
+ static const SwFlyFrame* s_pStack1;
+ static const SwFlyFrame* s_pStack2;
+ static const SwFlyFrame* s_pStack3;
+ static const SwFlyFrame* s_pStack4;
+ static const SwFlyFrame* s_pStack5;
+
+ const SwFlyFrame* m_pFly;
+ std::vector<Point> maObjPositions;
+
+public:
+ explicit SwOszControl( const SwFlyFrame *pFrame );
+ ~SwOszControl();
+ bool ChkOsz();
+ static bool IsInProgress( const SwFlyFrame *pFly );
+};
+
+}
+
+const SwFlyFrame* SwOszControl::s_pStack1 = nullptr;
+const SwFlyFrame* SwOszControl::s_pStack2 = nullptr;
+const SwFlyFrame* SwOszControl::s_pStack3 = nullptr;
+const SwFlyFrame* SwOszControl::s_pStack4 = nullptr;
+const SwFlyFrame* SwOszControl::s_pStack5 = nullptr;
+
+SwOszControl::SwOszControl(const SwFlyFrame* pFrame)
+ : m_pFly(pFrame)
+{
+ if (!SwOszControl::s_pStack1)
+ SwOszControl::s_pStack1 = m_pFly;
+ else if (!SwOszControl::s_pStack2)
+ SwOszControl::s_pStack2 = m_pFly;
+ else if (!SwOszControl::s_pStack3)
+ SwOszControl::s_pStack3 = m_pFly;
+ else if (!SwOszControl::s_pStack4)
+ SwOszControl::s_pStack4 = m_pFly;
+ else if (!SwOszControl::s_pStack5)
+ SwOszControl::s_pStack5 = m_pFly;
+}
+
+SwOszControl::~SwOszControl()
+{
+ if (SwOszControl::s_pStack1 == m_pFly)
+ SwOszControl::s_pStack1 = nullptr;
+ else if (SwOszControl::s_pStack2 == m_pFly)
+ SwOszControl::s_pStack2 = nullptr;
+ else if (SwOszControl::s_pStack3 == m_pFly)
+ SwOszControl::s_pStack3 = nullptr;
+ else if (SwOszControl::s_pStack4 == m_pFly)
+ SwOszControl::s_pStack4 = nullptr;
+ else if (SwOszControl::s_pStack5 == m_pFly)
+ SwOszControl::s_pStack5 = nullptr;
+ // #i3317#
+ maObjPositions.clear();
+}
+
+bool SwOszControl::IsInProgress( const SwFlyFrame *pFly )
+{
+ if (SwOszControl::s_pStack1 && !pFly->IsLowerOf(SwOszControl::s_pStack1))
+ return true;
+ if (SwOszControl::s_pStack2 && !pFly->IsLowerOf(SwOszControl::s_pStack2))
+ return true;
+ if (SwOszControl::s_pStack3 && !pFly->IsLowerOf(SwOszControl::s_pStack3))
+ return true;
+ if (SwOszControl::s_pStack4 && !pFly->IsLowerOf(SwOszControl::s_pStack4))
+ return true;
+ if (SwOszControl::s_pStack5 && !pFly->IsLowerOf(SwOszControl::s_pStack5))
+ return true;
+ return false;
+}
+
+bool SwOszControl::ChkOsz()
+{
+ bool bOscillationDetected = false;
+
+ if ( maObjPositions.size() == 20 )
+ {
+ // #i3317# position stack is full -> oscillation
+ bOscillationDetected = true;
+ }
+ else
+ {
+ Point aNewObjPos = m_pFly->GetObjRect().Pos();
+ for ( auto const & pt : maObjPositions )
+ {
+ if ( aNewObjPos == pt )
+ {
+ // position already occurred -> oscillation
+ bOscillationDetected = true;
+ break;
+ }
+ }
+ if ( !bOscillationDetected )
+ {
+ maObjPositions.push_back( aNewObjPos );
+ }
+ }
+
+ return bOscillationDetected;
+}
+
+/**
+|* With a paragraph-anchored fly it's absolutely possible that
+|* the anchor reacts to changes of the fly. To this reaction the fly must
+|* certainly react too. Sadly this can lead to oscillations; for example the
+|* fly wants to go down therefore the content can go up - this leads to a
+|* smaller TextFrame thus the fly needs to go up again whereby the text will
+|* get pushed down...
+|* To avoid such oscillations, a small position stack is built. If the fly
+|* reaches a position which it already had once, the action is stopped.
+|* To not run into problems, the stack is designed to hold five positions.
+|* If the stack flows over, the action is stopped too.
+|* Cancellation leads to the situation that the fly has a bad position in
+|* the end. In case of cancellation, the frame is set to automatic top
+|* alignment to not trigger a 'big oscillation' when calling from outside
+|* again.
+|*/
+void SwFlyAtContentFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ if ( !GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetVirtDrawObj()->GetLayer() ) )
+ {
+ return;
+ }
+
+ if ( SwOszControl::IsInProgress( this ) || IsLocked() || IsColLocked() )
+ return;
+
+ // #i28701# - use new method <GetPageFrame()>
+ if( !GetPageFrame() && GetAnchorFrame() && GetAnchorFrame()->IsInFly() )
+ {
+ SwFlyFrame* pFly = AnchorFrame()->FindFlyFrame();
+ SwPageFrame *pTmpPage = pFly ? pFly->FindPageFrame() : nullptr;
+ if( pTmpPage )
+ pTmpPage->AppendFlyToPage( this );
+ }
+ // #i28701# - use new method <GetPageFrame()>
+ if( !GetPageFrame() )
+ return;
+
+ bSetCompletePaintOnInvalidate = true;
+ {
+ SwFlyFrameFormat *pFormat = GetFormat();
+ const SwFormatFrameSize &rFrameSz = GetFormat()->GetFrameSize();
+ if( rFrameSz.GetHeightPercent() != SwFormatFrameSize::SYNCED &&
+ rFrameSz.GetHeightPercent() >= 100 )
+ {
+ pFormat->LockModify();
+ SwFormatSurround aMain( pFormat->GetSurround() );
+ if ( aMain.GetSurround() == css::text::WrapTextMode_NONE )
+ {
+ aMain.SetSurround( css::text::WrapTextMode_THROUGH );
+ pFormat->SetFormatAttr( aMain );
+ }
+ pFormat->UnlockModify();
+ }
+ }
+
+ SwOszControl aOszCntrl( this );
+
+ // #i43255#
+ // #i50356# - format the anchor frame, which
+ // contains the anchor position. E.g., for at-character anchored
+ // object this can be the follow frame of the anchor frame.
+ const bool bFormatAnchor =
+ !static_cast<const SwTextFrame*>( GetAnchorFrameContainingAnchPos() )->IsAnyJoinLocked() &&
+ !ConsiderObjWrapInfluenceOnObjPos() &&
+ !ConsiderObjWrapInfluenceOfOtherObjs();
+
+ const SwFrame* pFooter = GetAnchorFrame()->FindFooterOrHeader();
+ if( pFooter && !pFooter->IsFooterFrame() )
+ pFooter = nullptr;
+ bool bOsz = false;
+ bool bExtra = Lower() && Lower()->IsColumnFrame();
+ // #i3317# - boolean, to apply temporarily the
+ // 'straightforward positioning process' for the frame due to its
+ // overlapping with a previous column.
+ bool bConsiderWrapInfluenceDueToOverlapPrevCol( false );
+ // #i35911# - boolean, to apply temporarily the
+ // 'straightforward positioning process' for the frame due to fact
+ // that it causes the complete content of its layout environment
+ // to move forward.
+ // #i40444# - extend usage of this boolean:
+ // apply temporarily the 'straightforward positioning process' for
+ // the frame due to the fact that the frame clears the area for
+ // the anchor frame, thus it has to move forward.
+ bool bConsiderWrapInfluenceDueToMovedFwdAnchor( false );
+ do {
+ SwRectFnSet aRectFnSet(this);
+ Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
+ SwFlyFreeFrame::MakeAll(pRenderContext);
+
+ const bool bPosChgDueToOwnFormat =
+ aOldPos != aRectFnSet.GetPos(getFrameArea());
+ // #i3317#
+ if ( !ConsiderObjWrapInfluenceOnObjPos() &&
+ OverlapsPrevColumn() )
+ {
+ bConsiderWrapInfluenceDueToOverlapPrevCol = true;
+ }
+ // #i28701# - no format of anchor frame, if
+ // wrapping style influence is considered on object positioning
+ if ( bFormatAnchor )
+ {
+ SwTextFrame& rAnchPosAnchorFrame =
+ *GetAnchorFrameContainingAnchPos()->DynCastTextFrame();
+ // #i58182# - For the usage of new method
+ // <SwObjectFormatterTextFrame::CheckMovedFwdCondition(..)>
+ // to check move forward of anchor frame due to the object
+ // positioning it's needed to know, if the object is anchored
+ // at the master frame before the anchor frame is formatted.
+ const bool bAnchoredAtMaster(!rAnchPosAnchorFrame.IsFollow());
+
+ // #i56300#
+ // perform complete format of anchor text frame and its
+ // previous frames, which have become invalid due to the
+ // fly frame format.
+ SwObjectFormatterTextFrame::FormatAnchorFrameAndItsPrevs( rAnchPosAnchorFrame );
+ // #i35911#
+ // #i40444#
+ // #i58182# - usage of new method
+ // <SwObjectFormatterTextFrame::CheckMovedFwdCondition(..)>
+ sal_uInt32 nToPageNum( 0 );
+ bool bDummy( false );
+ bool bPageHasFlysAnchoredBelowThis(false);
+ if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition(
+// TODO: what if this fly moved bc it's in table? does sth prevent that?
+ *this, *GetPageFrame(),
+ bAnchoredAtMaster, nToPageNum, bDummy,
+ bPageHasFlysAnchoredBelowThis) )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ bConsiderWrapInfluenceDueToMovedFwdAnchor = true;
+ }
+ // mark anchor text frame
+ // directly, that it is moved forward by object positioning.
+ SwTextFrame* pAnchorTextFrame( static_cast<SwTextFrame*>(AnchorFrame()) );
+ bool bInsert( true );
+ sal_uInt32 nAnchorFrameToPageNum( 0 );
+ const SwDoc& rDoc = *(GetFrameFormat().GetDoc());
+ if ( SwLayouter::FrameMovedFwdByObjPos(
+ rDoc, *pAnchorTextFrame, nAnchorFrameToPageNum ) )
+ {
+ if ( nAnchorFrameToPageNum < nToPageNum )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::RemoveMovedFwdFrame(rDoc, *pAnchorTextFrame);
+ }
+ }
+ else
+ bInsert = false;
+ }
+ if ( bInsert )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::InsertMovedFwdFrame(rDoc, *pAnchorTextFrame,
+ nToPageNum);
+ }
+ }
+ }
+ }
+
+ if ( aOldPos != aRectFnSet.GetPos(getFrameArea()) ||
+ ( !isFrameAreaPositionValid() &&
+ ( pFooter || bPosChgDueToOwnFormat ) ) )
+ {
+ bOsz = aOszCntrl.ChkOsz();
+
+ // special loop prevention for dedicated document:
+ if ( bOsz &&
+ HasFixSize() && IsClipped() &&
+ GetAnchorFrame()->GetUpper()->IsCellFrame() )
+ {
+ SwFrameFormat* pFormat = GetFormat();
+ const SwFormatFrameSize& rFrameSz = pFormat->GetFrameSize();
+ if ( rFrameSz.GetWidthPercent() &&
+ rFrameSz.GetHeightPercent() == SwFormatFrameSize::SYNCED )
+ {
+ SwFormatSurround aSurround( pFormat->GetSurround() );
+ if ( aSurround.GetSurround() == css::text::WrapTextMode_NONE )
+ {
+ pFormat->LockModify();
+ aSurround.SetSurround( css::text::WrapTextMode_THROUGH );
+ pFormat->SetFormatAttr( aSurround );
+ pFormat->UnlockModify();
+ bOsz = false;
+ OSL_FAIL( "<SwFlyAtContentFrame::MakeAll()> - special loop prevention for dedicated document of b6403541 applied" );
+ }
+ }
+ }
+ }
+
+ if ( bExtra && Lower() && !Lower()->isFrameAreaPositionValid() )
+ {
+ // If a multi column frame leaves invalid columns because of
+ // a position change, we loop once more and format
+ // our content using FormatWidthCols again.
+ InvalidateSize_();
+ bExtra = false; // Ensure only one additional loop run
+ }
+ } while ( !isFrameAreaDefinitionValid() && !bOsz &&
+ // #i3317#
+ !bConsiderWrapInfluenceDueToOverlapPrevCol &&
+ // #i40444#
+ !bConsiderWrapInfluenceDueToMovedFwdAnchor &&
+ GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetVirtDrawObj()->GetLayer() ) );
+
+ // #i3317# - instead of attribute change apply
+ // temporarily the 'straightforward positioning process'.
+ // #i80924#
+ // handle special case during splitting of table rows
+ if ( bConsiderWrapInfluenceDueToMovedFwdAnchor &&
+ GetAnchorFrame()->IsInTab() &&
+ GetAnchorFrame()->IsInFollowFlowRow() )
+ {
+ const SwFrame* pCellFrame = GetAnchorFrame();
+ while ( pCellFrame && !pCellFrame->IsCellFrame() )
+ {
+ pCellFrame = pCellFrame->GetUpper();
+ }
+ if ( pCellFrame )
+ {
+ SwRectFnSet aRectFnSet(pCellFrame);
+ if ( aRectFnSet.GetTop(pCellFrame->getFrameArea()) == 0 &&
+ aRectFnSet.GetHeight(pCellFrame->getFrameArea()) == 0 )
+ {
+ bConsiderWrapInfluenceDueToMovedFwdAnchor = false;
+ }
+ }
+ }
+ // tdf#137803: Fix the position of the shape during autoSize
+ SwFrameFormat* pShapeFormat
+ = SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT);
+ // FIXME: According to tdf37153, ignore FollowTextFlow objs, because
+ // wrong position will applied in that case. FollowTextFlow needs fix.
+ if (pShapeFormat && !pShapeFormat->GetFollowTextFlow().GetValue() &&
+ SwTextBoxHelper::getProperty(pShapeFormat,
+ UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT).hasValue() &&
+ SwTextBoxHelper::getProperty(pShapeFormat,
+ UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT).get<bool>() )
+ {
+ // get the text area of the shape
+ const tools::Rectangle aTextRectangle
+ = SwTextBoxHelper::getRelativeTextRectangle(pShapeFormat->FindRealSdrObject());
+ // get the original textframe position
+ SwFormatHoriOrient aHOri = pShapeFormat->GetHoriOrient();
+ SwFormatVertOrient aVOri = pShapeFormat->GetVertOrient();
+ // calc the right position of the shape depending on text area
+ aHOri.SetPos(aHOri.GetPos() + aTextRectangle.Left());
+ aVOri.SetPos(aVOri.GetPos() + aTextRectangle.Top());
+ // save the new position for the shape
+ auto pFormat = GetFormat();
+ const bool bLocked = pFormat->IsModifyLocked();
+ if (!bLocked)
+ pFormat->LockModify();
+ pFormat->SetFormatAttr(aHOri);
+ pFormat->SetFormatAttr(aVOri);
+ if (!bLocked)
+ pFormat->UnlockModify();
+ }
+ if ( bOsz || bConsiderWrapInfluenceDueToOverlapPrevCol ||
+ // #i40444#
+ bConsiderWrapInfluenceDueToMovedFwdAnchor )
+ {
+ SetTmpConsiderWrapInfluence( true );
+ SetRestartLayoutProcess( true );
+ SetTmpConsiderWrapInfluenceOfOtherObjs();
+ }
+ bSetCompletePaintOnInvalidate = false;
+}
+
+/** method to determine, if a <MakeAll()> on the Writer fly frame is possible
+
+ #i28701#
+*/
+bool SwFlyAtContentFrame::IsFormatPossible() const
+{
+ return SwFlyFreeFrame::IsFormatPossible() &&
+ !SwOszControl::IsInProgress( this );
+}
+
+namespace {
+
+class SwDistance
+{
+public:
+ SwTwips m_nMain, m_nSub;
+ SwDistance()
+ : m_nMain(0)
+ , m_nSub(0)
+ {
+ }
+ bool operator<( const SwDistance& rTwo ) const
+ {
+ return m_nMain < rTwo.m_nMain
+ || (m_nMain == rTwo.m_nMain && m_nSub && rTwo.m_nSub && m_nSub < rTwo.m_nSub);
+ }
+ bool operator<=( const SwDistance& rTwo ) const
+ {
+ return m_nMain < rTwo.m_nMain
+ || (m_nMain == rTwo.m_nMain
+ && (!m_nSub || !rTwo.m_nSub || m_nSub <= rTwo.m_nSub));
+ }
+};
+
+}
+
+static const SwFrame * lcl_CalcDownDist( SwDistance &rRet,
+ const Point &rPt,
+ const SwContentFrame *pCnt )
+{
+ rRet.m_nSub = 0;
+ //If the point stays inside the Cnt everything is clear already; the Content
+ //automatically has a distance of 0.
+ if ( pCnt->getFrameArea().Contains( rPt ) )
+ {
+ rRet.m_nMain = 0;
+ return pCnt;
+ }
+ else
+ {
+ const SwLayoutFrame *pUp = pCnt->IsInTab() ? pCnt->FindTabFrame()->GetUpper() : pCnt->GetUpper();
+ // single column sections need to interconnect to their upper
+ while( pUp->IsSctFrame() )
+ pUp = pUp->GetUpper();
+ const bool bVert = pUp->IsVertical();
+
+ const bool bVertL2R = pUp->IsVertLR();
+
+ //Follow the text flow.
+ // #i70582#
+ // --> OD 2009-03-05 - adopted for Support for Classical Mongolian Script
+ const SwTwips nTopForObjPos = lcl_GetTopForObjPos(pCnt, bVert, bVertL2R);
+ if ( pUp->getFrameArea().Contains( rPt ) )
+ {
+ // <rPt> point is inside environment of given content frame
+ // #i70582#
+ if( bVert )
+ {
+ if ( bVertL2R )
+ rRet.m_nMain = rPt.X() - nTopForObjPos;
+ else
+ rRet.m_nMain = nTopForObjPos - rPt.X();
+ }
+ else
+ rRet.m_nMain = rPt.Y() - nTopForObjPos;
+ return pCnt;
+ }
+ else if ( rPt.Y() <= pUp->getFrameArea().Top() )
+ {
+ // <rPt> point is above environment of given content frame
+ // correct for vertical layout?
+ rRet.m_nMain = LONG_MAX;
+ }
+ else if( rPt.X() < pUp->getFrameArea().Left() &&
+ rPt.Y() <= ( bVert ? pUp->getFrameArea().Top() : pUp->getFrameArea().Bottom() ) )
+ {
+ // <rPt> point is left of environment of given content frame
+ // seems not to be correct for vertical layout!?
+ const SwFrame *pLay = pUp->GetLeaf( MAKEPAGE_NONE, false, pCnt );
+ if( !pLay ||
+ (bVert && (pLay->getFrameArea().Top() + pLay->getFramePrintArea().Bottom()) <rPt.Y())||
+ (!bVert && (pLay->getFrameArea().Left() + pLay->getFramePrintArea().Right())<rPt.X()) )
+ {
+ // <rPt> point is in left border of environment
+ // #i70582#
+ if( bVert )
+ {
+ if ( bVertL2R )
+ rRet.m_nMain = rPt.X() - nTopForObjPos;
+ else
+ rRet.m_nMain = nTopForObjPos - rPt.X();
+ }
+ else
+ rRet.m_nMain = rPt.Y() - nTopForObjPos;
+ return pCnt;
+ }
+ else
+ rRet.m_nMain = LONG_MAX;
+ }
+ else
+ {
+ rRet.m_nMain
+ = bVert ? (bVertL2R
+ ? ((pUp->getFrameArea().Left() + pUp->getFramePrintArea().Right())
+ - nTopForObjPos)
+ : (nTopForObjPos
+ - (pUp->getFrameArea().Left() + pUp->getFramePrintArea().Left())))
+ : ((pUp->getFrameArea().Top() + pUp->getFramePrintArea().Bottom())
+ - nTopForObjPos);
+
+ const SwFrame *pPre = pCnt;
+ const SwFrame *pLay = pUp->GetLeaf( MAKEPAGE_NONE, true, pCnt );
+ SwTwips nFrameTop = 0;
+ SwTwips nPrtHeight = 0;
+ bool bSct = false;
+ const SwSectionFrame *pSect = pUp->FindSctFrame();
+ if( pSect )
+ {
+ rRet.m_nSub = rRet.m_nMain;
+ rRet.m_nMain = 0;
+ }
+ if( pSect && !pSect->IsAnLower( pLay ) )
+ {
+ bSct = false;
+ const SwSectionFrame* pNxtSect = pLay ? pLay->FindSctFrame() : nullptr;
+ if (pSect->IsAnFollow(pNxtSect) && pLay)
+ {
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ nFrameTop = pLay->getFrameArea().Left();
+ else
+ nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Top();
+ nPrtHeight = pLay->getFramePrintArea().Height();
+ }
+ pSect = pNxtSect;
+ }
+ else
+ {
+ pLay = pSect->GetUpper();
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ {
+ nFrameTop = pSect->getFrameArea().Right();
+ nPrtHeight = pLay->getFrameArea().Left() + pLay->getFramePrintArea().Left()
+ + pLay->getFramePrintArea().Width() - pSect->getFrameArea().Left()
+ - pSect->getFrameArea().Width();
+ }
+ else
+ {
+ nFrameTop = pSect->getFrameArea().Left();
+ nPrtHeight = pSect->getFrameArea().Left() - pLay->getFrameArea().Left()
+ - pLay->getFramePrintArea().Left();
+ }
+ }
+ else
+ {
+ nFrameTop = pSect->getFrameArea().Bottom();
+ nPrtHeight = pLay->getFrameArea().Top() + pLay->getFramePrintArea().Top()
+ + pLay->getFramePrintArea().Height() - pSect->getFrameArea().Top()
+ - pSect->getFrameArea().Height();
+ }
+ pSect = nullptr;
+ }
+ }
+ else if( pLay )
+ {
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ {
+ nFrameTop = pLay->getFrameArea().Left();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Top();
+ nPrtHeight = pLay->getFramePrintArea().Height();
+ }
+ bSct = nullptr != pSect;
+ }
+ while ( pLay && !pLay->getFrameArea().Contains( rPt ) &&
+ ( pLay->getFrameArea().Top() <= rPt.Y() || pLay->IsInFly() ||
+ ( pLay->IsInSct() &&
+ pLay->FindSctFrame()->GetUpper()->getFrameArea().Top() <= rPt.Y())) )
+ {
+ if ( pLay->IsFootnoteContFrame() )
+ {
+ if ( !static_cast<const SwLayoutFrame*>(pLay)->Lower() )
+ {
+ SwFrame *pDel = const_cast<SwFrame*>(pLay);
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ return pPre;
+ }
+ return nullptr;
+ }
+ else
+ {
+ if( bSct || pSect )
+ rRet.m_nSub += nPrtHeight;
+ else
+ rRet.m_nMain += nPrtHeight;
+ pPre = pLay;
+ pLay = pLay->GetLeaf( MAKEPAGE_NONE, true, pCnt );
+ if( pSect && !pSect->IsAnLower( pLay ) )
+ { // If we're leaving a SwSectionFrame, the next Leaf-Frame
+ // is the part of the upper below the SectionFrame.
+ const SwSectionFrame* pNxtSect = pLay ?
+ pLay->FindSctFrame() : nullptr;
+ bSct = false;
+ if (pLay && pSect->IsAnFollow(pNxtSect))
+ {
+ pSect = pNxtSect;
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ {
+ nFrameTop = pLay->getFrameArea().Left();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Top();
+ nPrtHeight = pLay->getFramePrintArea().Height();
+ }
+ }
+ else
+ {
+ pLay = pSect->GetUpper();
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ {
+ nFrameTop = pSect->getFrameArea().Right();
+ nPrtHeight = pLay->getFrameArea().Left()+pLay->getFramePrintArea().Left()
+ + pLay->getFramePrintArea().Width() - pSect->getFrameArea().Left()
+ - pSect->getFrameArea().Width();
+ }
+ else
+ {
+ nFrameTop = pSect->getFrameArea().Left();
+ nPrtHeight = pSect->getFrameArea().Left() -
+ pLay->getFrameArea().Left() - pLay->getFramePrintArea().Left();
+ }
+ }
+ else
+ {
+ nFrameTop = pSect->getFrameArea().Bottom();
+ nPrtHeight = pLay->getFrameArea().Top()+pLay->getFramePrintArea().Top()
+ + pLay->getFramePrintArea().Height() - pSect->getFrameArea().Top()
+ - pSect->getFrameArea().Height();
+ }
+ pSect = nullptr;
+ }
+ }
+ else if( pLay )
+ {
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ {
+ nFrameTop = pLay->getFrameArea().Left();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Top();
+ nPrtHeight = pLay->getFramePrintArea().Height();
+ }
+ bSct = nullptr != pSect;
+ }
+ }
+ }
+ if ( pLay )
+ {
+ if ( pLay->getFrameArea().Contains( rPt ) )
+ {
+ SwTwips nDiff = pLay->IsVertical() ? ( pLay->IsVertLR() ? ( rPt.X() - nFrameTop ) : ( nFrameTop - rPt.X() ) )
+ : ( rPt.Y() - nFrameTop );
+ if( bSct || pSect )
+ rRet.m_nSub += nDiff;
+ else
+ rRet.m_nMain += nDiff;
+ }
+ if ( pLay->IsFootnoteContFrame() && !static_cast<const SwLayoutFrame*>(pLay)->Lower() )
+ {
+ SwFrame *pDel = const_cast<SwFrame*>(pLay);
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ return nullptr;
+ }
+ return pLay;
+ }
+ else
+ rRet.m_nMain = LONG_MAX;
+ }
+ }
+ return nullptr;
+}
+
+static sal_uInt64 lcl_FindCntDiff( const Point &rPt, const SwLayoutFrame *pLay,
+ const SwContentFrame *& rpCnt,
+ const bool bBody, const bool bFootnote )
+{
+ // Searches below pLay the nearest Cnt to the point. The reference point of
+ //the Contents is always the left upper corner.
+ //The Cnt should preferably be above the point.
+
+ rpCnt = nullptr;
+ sal_uInt64 nDistance = SAL_MAX_UINT64;
+ sal_uInt64 nNearest = SAL_MAX_UINT64;
+ const SwContentFrame *pCnt = pLay ? pLay->ContainsContent() : nullptr;
+
+ while ( pCnt && (bBody != pCnt->IsInDocBody() || bFootnote != pCnt->IsInFootnote()))
+ {
+ pCnt = pCnt->GetNextContentFrame();
+ if ( !pLay->IsAnLower( pCnt ) )
+ pCnt = nullptr;
+ }
+ const SwContentFrame *pNearest = pCnt;
+ if ( pCnt )
+ {
+ do
+ {
+ //Calculate the distance between those two points.
+ //'delta' X^2 + 'delta' Y^2 = 'distance'^2
+ sal_uInt64 dX = std::max( pCnt->getFrameArea().Left(), rPt.X() ) -
+ std::min( pCnt->getFrameArea().Left(), rPt.X() ),
+ dY = std::max( pCnt->getFrameArea().Top(), rPt.Y() ) -
+ std::min( pCnt->getFrameArea().Top(), rPt.Y() );
+ // square of the difference will do fine here
+ const sal_uInt64 nDiff = (dX * dX) + (dY * dY);
+ if ( pCnt->getFrameArea().Top() <= rPt.Y() )
+ {
+ if ( nDiff < nDistance )
+ {
+ //This one is the nearer one
+ nDistance = nNearest = nDiff;
+ rpCnt = pNearest = pCnt;
+ }
+ }
+ else if ( nDiff < nNearest )
+ {
+ nNearest = nDiff;
+ pNearest = pCnt;
+ }
+ pCnt = pCnt->GetNextContentFrame();
+ while ( pCnt &&
+ (bBody != pCnt->IsInDocBody() || bFootnote != pCnt->IsInFootnote()))
+ pCnt = pCnt->GetNextContentFrame();
+
+ } while ( pCnt && pLay->IsAnLower( pCnt ) );
+ }
+ if (nDistance == SAL_MAX_UINT64)
+ { rpCnt = pNearest;
+ return nNearest;
+ }
+ return nDistance;
+}
+
+static const SwContentFrame * lcl_FindCnt( const Point &rPt, const SwContentFrame *pCnt,
+ const bool bBody, const bool bFootnote )
+{
+ //Starting from pCnt searches the ContentFrame whose left upper corner is the
+ //nearest to the point.
+ //Always returns a ContentFrame.
+
+ //First the nearest Content inside the page which contains the Content is
+ //searched. Starting from this page the pages in both directions need to
+ //be considered. If possible a Content is returned whose Y-position is
+ //above the point.
+ const SwContentFrame *pRet, *pNew;
+ const SwLayoutFrame *pLay = pCnt->FindPageFrame();
+ sal_uInt64 nDist; // not sure if a sal_Int32 would be enough?
+
+ nDist = ::lcl_FindCntDiff( rPt, pLay, pNew, bBody, bFootnote );
+ if ( pNew )
+ pRet = pNew;
+ else
+ { pRet = pCnt;
+ nDist = SAL_MAX_UINT64;
+ }
+ const SwContentFrame *pNearest = pRet;
+ sal_uInt64 nNearest = nDist;
+
+ if ( pLay )
+ {
+ const SwLayoutFrame *pPge = pLay;
+ sal_uInt64 nOldNew = SAL_MAX_UINT64;
+ for ( int i = 0; pPge->GetPrev() && (i < 3); ++i )
+ {
+ pPge = static_cast<const SwLayoutFrame*>(pPge->GetPrev());
+ const sal_uInt64 nNew = ::lcl_FindCntDiff( rPt, pPge, pNew, bBody, bFootnote );
+ if ( nNew < nDist )
+ {
+ if ( pNew->getFrameArea().Top() <= rPt.Y() )
+ {
+ pRet = pNearest = pNew;
+ nDist = nNearest = nNew;
+ }
+ else if ( nNew < nNearest )
+ {
+ pNearest = pNew;
+ nNearest = nNew;
+ }
+ }
+ else if (nOldNew != SAL_MAX_UINT64 && nNew > nOldNew)
+ break;
+ else
+ nOldNew = nNew;
+
+ }
+ pPge = pLay;
+ nOldNew = SAL_MAX_UINT64;
+ for ( int j = 0; pPge->GetNext() && (j < 3); ++j )
+ {
+ pPge = static_cast<const SwLayoutFrame*>(pPge->GetNext());
+ const sal_uInt64 nNew = ::lcl_FindCntDiff( rPt, pPge, pNew, bBody, bFootnote );
+ if ( nNew < nDist )
+ {
+ if ( pNew->getFrameArea().Top() <= rPt.Y() )
+ {
+ pRet = pNearest = pNew;
+ nDist = nNearest = nNew;
+ }
+ else if ( nNew < nNearest )
+ {
+ pNearest = pNew;
+ nNearest = nNew;
+ }
+ }
+ else if (nOldNew != SAL_MAX_UINT64 && nNew > nOldNew)
+ break;
+ else
+ nOldNew = nNew;
+ }
+ }
+ if ( pRet->getFrameArea().Top() > rPt.Y() )
+ return pNearest;
+ else
+ return pRet;
+}
+
+static void lcl_PointToPrt( Point &rPoint, const SwFrame *pFrame )
+{
+ SwRect aTmp( pFrame->getFramePrintArea() );
+ aTmp += pFrame->getFrameArea().Pos();
+ if ( rPoint.getX() < aTmp.Left() )
+ rPoint.setX(aTmp.Left());
+ else if ( rPoint.getX() > aTmp.Right() )
+ rPoint.setX(aTmp.Right());
+ if ( rPoint.getY() < aTmp.Top() )
+ rPoint.setY(aTmp.Top());
+ else if ( rPoint.getY() > aTmp.Bottom() )
+ rPoint.setY(aTmp.Bottom());
+
+}
+
+/** Searches an anchor for paragraph bound objects starting from pOldAnch.
+ *
+ * This is used to show anchors as well as changing anchors
+ * when dragging paragraph bound objects.
+ */
+const SwContentFrame *FindAnchor( const SwFrame *pOldAnch, const Point &rNew,
+ const bool bBodyOnly )
+{
+ //Search the nearest Cnt around the given document position in the text
+ //flow. The given anchor is the starting Frame.
+ const SwContentFrame* pCnt;
+ if ( pOldAnch->IsContentFrame() )
+ {
+ pCnt = static_cast<const SwContentFrame*>(pOldAnch);
+ }
+ else
+ {
+ Point aTmp( rNew );
+ const SwLayoutFrame *pTmpLay = static_cast<const SwLayoutFrame*>(pOldAnch);
+ if( pTmpLay->IsRootFrame() )
+ {
+ SwRect aTmpRect( aTmp, Size(0,0) );
+ pTmpLay = static_cast<const SwLayoutFrame*>(::FindPage( aTmpRect, pTmpLay->Lower() ));
+ }
+ pCnt = pTmpLay->GetContentPos( aTmp, false, bBodyOnly );
+ }
+
+ //Take care to use meaningful ranges during search. This means to not enter
+ //or leave header/footer in this case.
+ const bool bBody = pCnt->IsInDocBody() || bBodyOnly;
+ const bool bFootnote = !bBodyOnly && pCnt->IsInFootnote();
+
+ Point aNew( rNew );
+ if ( bBody )
+ {
+ //#38848 drag from page margin into the body.
+ const SwFrame *pPage = pCnt->FindPageFrame();
+ ::lcl_PointToPrt( aNew, pPage->GetUpper() );
+ SwRect aTmp( aNew, Size( 0, 0 ) );
+ pPage = ::FindPage( aTmp, pPage );
+ ::lcl_PointToPrt( aNew, pPage );
+ }
+
+ if ( pCnt->IsInDocBody() == bBody && pCnt->getFrameArea().Contains( aNew ) )
+ return pCnt;
+ else if ( pOldAnch->IsInDocBody() || pOldAnch->IsPageFrame() )
+ {
+ // Maybe the selected anchor is on the same page as the current anchor.
+ // With this we won't run into problems with the columns.
+ Point aTmp( aNew );
+ const SwContentFrame *pTmp = pCnt->FindPageFrame()->
+ GetContentPos( aTmp, false, true );
+ if ( pTmp && pTmp->getFrameArea().Contains( aNew ) )
+ return pTmp;
+ }
+
+ //Starting from the anchor we now search in both directions until we found
+ //the nearest one respectively.
+ //Not the direct distance is relevant but the distance which needs to be
+ //traveled through the text flow.
+ const SwContentFrame *pUpLst;
+ const SwContentFrame *pUpFrame = pCnt;
+ SwDistance nUp, nUpLst;
+ ::lcl_CalcDownDist( nUp, aNew, pUpFrame );
+ SwDistance nDown = nUp;
+ bool bNegAllowed = true;// Make it possible to leave the negative section once.
+ do
+ {
+ pUpLst = pUpFrame; nUpLst = nUp;
+ pUpFrame = pUpLst->GetPrevContentFrame();
+ while ( pUpFrame &&
+ (bBody != pUpFrame->IsInDocBody() || bFootnote != pUpFrame->IsInFootnote()))
+ pUpFrame = pUpFrame->GetPrevContentFrame();
+ if ( pUpFrame )
+ {
+ ::lcl_CalcDownDist( nUp, aNew, pUpFrame );
+ //It makes sense to search further, if the distance grows inside
+ //a table.
+ if ( pUpLst->IsInTab() && pUpFrame->IsInTab() )
+ {
+ while ( pUpFrame && ((nUpLst < nUp && pUpFrame->IsInTab()) ||
+ bBody != pUpFrame->IsInDocBody()) )
+ {
+ pUpFrame = pUpFrame->GetPrevContentFrame();
+ if ( pUpFrame )
+ ::lcl_CalcDownDist( nUp, aNew, pUpFrame );
+ }
+ }
+ }
+ if ( !pUpFrame )
+ nUp.m_nMain = LONG_MAX;
+ if (nUp.m_nMain >= 0 && LONG_MAX != nUp.m_nMain)
+ {
+ bNegAllowed = false;
+ if (nUpLst.m_nMain < 0) //don't take the wrong one, if the value
+ //just changed from negative to positive.
+ { pUpLst = pUpFrame;
+ nUpLst = nUp;
+ }
+ }
+ } while (pUpFrame && ((bNegAllowed && nUp.m_nMain < 0) || (nUp <= nUpLst)));
+
+ const SwContentFrame *pDownLst;
+ const SwContentFrame *pDownFrame = pCnt;
+ SwDistance nDownLst;
+ if (nDown.m_nMain < 0)
+ nDown.m_nMain = LONG_MAX;
+ do
+ {
+ pDownLst = pDownFrame; nDownLst = nDown;
+ pDownFrame = pDownLst->GetNextContentFrame();
+ while ( pDownFrame &&
+ (bBody != pDownFrame->IsInDocBody() || bFootnote != pDownFrame->IsInFootnote()))
+ pDownFrame = pDownFrame->GetNextContentFrame();
+ if ( pDownFrame )
+ {
+ ::lcl_CalcDownDist( nDown, aNew, pDownFrame );
+ if (nDown.m_nMain < 0)
+ nDown.m_nMain = LONG_MAX;
+ //It makes sense to search further, if the distance grows inside
+ //a table.
+ if ( pDownLst->IsInTab() && pDownFrame->IsInTab() )
+ {
+ while (pDownFrame
+ && ((nDown.m_nMain != LONG_MAX && pDownFrame->IsInTab())
+ || bBody != pDownFrame->IsInDocBody()))
+ {
+ pDownFrame = pDownFrame->GetNextContentFrame();
+ if ( pDownFrame )
+ ::lcl_CalcDownDist( nDown, aNew, pDownFrame );
+ if (nDown.m_nMain < 0)
+ nDown.m_nMain = LONG_MAX;
+ }
+ }
+ }
+ if ( !pDownFrame )
+ nDown.m_nMain = LONG_MAX;
+
+ } while (pDownFrame && nDown <= nDownLst && nDown.m_nMain != LONG_MAX
+ && nDownLst.m_nMain != LONG_MAX);
+
+ //If we couldn't find one in both directions, we'll search the Content whose
+ //left upper corner is the nearest to the point. Such a situation may
+ //happen, if the point doesn't lay in the text flow but in any margin.
+ if (nDownLst.m_nMain == LONG_MAX && nUpLst.m_nMain == LONG_MAX)
+ {
+ // If an OLE objects, which is contained in a fly frame
+ // is resized in inplace mode and the new Position is outside the
+ // fly frame, we do not want to leave our fly frame.
+ if ( pCnt->IsInFly() )
+ return pCnt;
+
+ return ::lcl_FindCnt( aNew, pCnt, bBody, bFootnote );
+ }
+ else
+ return nDownLst < nUpLst ? pDownLst : pUpLst;
+}
+
+void SwFlyAtContentFrame::SetAbsPos( const Point &rNew )
+{
+ SwPageFrame *pOldPage = FindPageFrame();
+ const SwRect aOld( GetObjRectWithSpaces() );
+ Point aNew( rNew );
+
+ if( ( GetAnchorFrame()->IsVertical() && !GetAnchorFrame()->IsVertLR() ) || GetAnchorFrame()->IsRightToLeft() )
+ aNew.setX(aNew.getX() + getFrameArea().Width());
+ SwContentFrame *pCnt = const_cast<SwContentFrame*>(::FindAnchor( GetAnchorFrame(), aNew ));
+ if( pCnt->IsProtected() )
+ pCnt = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(GetAnchorFrame()));
+
+ SwPageFrame *pTmpPage = nullptr;
+ const bool bVert = pCnt->IsVertical();
+
+ const bool bVertL2R = pCnt->IsVertLR();
+ const bool bRTL = pCnt->IsRightToLeft();
+
+ if( ( !bVert != !GetAnchorFrame()->IsVertical() ) ||
+ ( !bRTL != !GetAnchorFrame()->IsRightToLeft() ) )
+ {
+ if( bVert || bRTL )
+ aNew.setX(aNew.getX() + getFrameArea().Width());
+ else
+ aNew.setX(aNew.getX() - getFrameArea().Width());
+ }
+
+ if ( pCnt->IsInDocBody() )
+ {
+ //#38848 drag from page margin into the body.
+ pTmpPage = pCnt->FindPageFrame();
+ ::lcl_PointToPrt( aNew, pTmpPage->GetUpper() );
+ SwRect aTmp( aNew, Size( 0, 0 ) );
+ pTmpPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( aTmp, pTmpPage )));
+ ::lcl_PointToPrt( aNew, pTmpPage );
+ }
+
+ //Setup RelPos, only invalidate if requested.
+ //rNew is an absolute position. We need to calculate the distance from rNew
+ //to the anchor inside the text flow to correctly set RelPos.
+//!!!!!We can optimize here: FindAnchor could also return RelPos!
+ const SwFrame *pFrame = nullptr;
+ SwTwips nY;
+ if ( pCnt->getFrameArea().Contains( aNew ) )
+ {
+ // #i70582#
+ if ( bVert )
+ {
+ nY = pCnt->getFrameArea().Left() - rNew.X();
+ if ( bVertL2R )
+ nY = -nY;
+ else
+ nY += pCnt->getFrameArea().Width() - getFrameArea().Width();
+ nY -= pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ }
+ else
+ nY = rNew.Y() - pCnt->getFrameArea().Top() - pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ }
+ else
+ {
+ SwDistance aDist;
+ pFrame = ::lcl_CalcDownDist( aDist, aNew, pCnt );
+ nY = aDist.m_nMain + aDist.m_nSub;
+ }
+
+ SwTwips nX = 0;
+
+ if ( pCnt->IsFollow() )
+ {
+ // Flys are never attached to the follow but always to the master,
+ // which we're going to search now.
+ const SwContentFrame *pOriginal = pCnt;
+ const SwContentFrame *pFollow = pCnt;
+ while ( pCnt->IsFollow() )
+ {
+ do
+ {
+ SwContentFrame* pPrev = pCnt->GetPrevContentFrame();
+ if (!pPrev)
+ {
+ SAL_WARN("sw.core", "very unexpected missing PrevContentFrame");
+ break;
+ }
+ pCnt = pPrev;
+ }
+ while ( pCnt->GetFollow() != pFollow );
+ pFollow = pCnt;
+ }
+ SwTwips nDiff = 0;
+ do
+ { const SwFrame *pUp = pFollow->GetUpper();
+ if( pUp->IsVertical() )
+ {
+ if ( pUp->IsVertLR() )
+ nDiff += pUp->getFramePrintArea().Width() - pFollow->GetRelPos().getX();
+ else
+ nDiff += pFollow->getFrameArea().Left() + pFollow->getFrameArea().Width()
+ - pUp->getFrameArea().Left() - pUp->getFramePrintArea().Left();
+ }
+ else
+ nDiff += pUp->getFramePrintArea().Height() - pFollow->GetRelPos().Y();
+ pFollow = pFollow->GetFollow();
+ } while ( pFollow != pOriginal );
+ nY += nDiff;
+ if( bVert )
+ nX = pCnt->getFrameArea().Top() - pOriginal->getFrameArea().Top();
+ else
+ nX = pCnt->getFrameArea().Left() - pOriginal->getFrameArea().Left();
+ }
+
+ if ( nY == LONG_MAX )
+ {
+ // #i70582#
+ const SwTwips nTopForObjPos = lcl_GetTopForObjPos(pCnt, bVert, bVertL2R);
+ if( bVert )
+ {
+ if ( bVertL2R )
+ nY = rNew.X() - nTopForObjPos;
+ else
+ nY = nTopForObjPos - rNew.X();
+ }
+ else
+ {
+ nY = rNew.Y() - nTopForObjPos;
+ }
+ }
+
+ SwFlyFrameFormat *pFormat = GetFormat();
+
+ if( bVert )
+ {
+ if( !pFrame )
+ nX += rNew.Y() - pCnt->getFrameArea().Top();
+ else
+ nX = rNew.Y() - pFrame->getFrameArea().Top();
+ }
+ else
+ {
+ if( !pFrame )
+ {
+ if ( pCnt->IsRightToLeft() )
+ nX += pCnt->getFrameArea().Right() - rNew.X() - getFrameArea().Width();
+ else
+ nX += rNew.X() - pCnt->getFrameArea().Left();
+ }
+ else
+ {
+ if ( pFrame->IsRightToLeft() )
+ nX += pFrame->getFrameArea().Right() - rNew.X() - getFrameArea().Width();
+ else
+ nX = rNew.X() - pFrame->getFrameArea().Left();
+ }
+ }
+ GetFormat()->GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+
+ if( pCnt != GetAnchorFrame() || ( IsAutoPos() && pCnt->IsTextFrame() &&
+ GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)) )
+ {
+ //Set the anchor attribute according to the new Cnt.
+ SwFormatAnchor aAnch( pFormat->GetAnchor() );
+ SwPosition pos = *aAnch.GetContentAnchor();
+ if( IsAutoPos() && pCnt->IsTextFrame() )
+ {
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pCnt));
+ SwCursorMoveState eTmpState( CursorMoveState::SetOnlyText );
+ Point aPt( rNew );
+ if( pCnt->GetModelPositionForViewPoint( &pos, aPt, &eTmpState )
+ && FrameContainsNode(*pTextFrame, pos.nNode.GetIndex()))
+ {
+ const SwTextAttr *const pTextInputField =
+ pos.nNode.GetNode().GetTextNode()->GetTextAttrAt(
+ pos.nContent.GetIndex(), RES_TXTATR_INPUTFIELD, SwTextNode::PARENT );
+ if (pTextInputField != nullptr)
+ {
+ pos.nContent = pTextInputField->GetStart();
+ }
+ ResetLastCharRectHeight();
+ if( text::RelOrientation::CHAR == pFormat->GetVertOrient().GetRelationOrient() )
+ nY = LONG_MAX;
+ if( text::RelOrientation::CHAR == pFormat->GetHoriOrient().GetRelationOrient() )
+ nX = LONG_MAX;
+ }
+ else
+ {
+ pos = pTextFrame->MapViewToModelPos(TextFrameIndex(0));
+ }
+ }
+ else if (pCnt->IsTextFrame())
+ {
+ pos = static_cast<SwTextFrame const*>(pCnt)->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else // is that even possible? maybe if there was a change of anchor type from AT_FLY or something?
+ {
+ assert(pCnt->IsNoTextFrame());
+ pos.nNode = *static_cast<SwNoTextFrame*>(pCnt)->GetNode();
+ pos.nContent.Assign(static_cast<SwNoTextFrame*>(pCnt)->GetNode(), 0);
+ }
+ aAnch.SetAnchor( &pos );
+
+ // handle change of anchor node:
+ // if count of the anchor frame also change, the fly frames have to be
+ // re-created. Thus, delete all fly frames except the <this> before the
+ // anchor attribute is change and re-create them afterwards.
+ {
+ SwHandleAnchorNodeChg aHandleAnchorNodeChg( *pFormat, aAnch, this );
+ pFormat->GetDoc()->SetAttr( aAnch, *pFormat );
+ }
+ }
+ else if ( pTmpPage && pTmpPage != GetPageFrame() )
+ GetPageFrame()->MoveFly( this, pTmpPage );
+
+ const Point aRelPos = bVert ? Point( -nY, nX ) : Point( nX, nY );
+ ChgRelPos( aRelPos );
+ GetFormat()->GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+
+ if ( pOldPage != FindPageFrame() )
+ ::Notify_Background( GetVirtDrawObj(), pOldPage, aOld, PrepareHint::FlyFrameLeave, false );
+}
+
+/** method to assure that anchored object is registered at the correct
+ page frame
+
+ #i28701#
+ takes over functionality of deleted method <SwFlyAtContentFrame::AssertPage()>
+*/
+void SwFlyAtContentFrame::RegisterAtCorrectPage()
+{
+ SwPageFrame* pPageFrame( nullptr );
+ if ( GetVertPosOrientFrame() )
+ {
+ pPageFrame = const_cast<SwPageFrame*>(GetVertPosOrientFrame()->FindPageFrame());
+ }
+ if ( pPageFrame && GetPageFrame() != pPageFrame )
+ {
+ RegisterAtPage(*pPageFrame);
+ }
+}
+
+void SwFlyAtContentFrame::RegisterAtPage(SwPageFrame & rPageFrame)
+{
+ assert(GetPageFrame() != &rPageFrame);
+ if (GetPageFrame())
+ {
+ GetPageFrame()->MoveFly( this, &rPageFrame );
+ }
+ else
+ {
+ rPageFrame.AppendFlyToPage( this );
+ }
+}
+
+// #i26791#
+void SwFlyAtContentFrame::MakeObjPos()
+{
+ // if fly frame position is valid, nothing is to do. Thus, return
+ if ( isFrameAreaPositionValid() )
+ {
+ return;
+ }
+
+ // #i26791# - validate position flag here.
+ setFrameAreaPositionValid(true);
+
+ // #i35911# - no calculation of new position, if
+ // anchored object is marked that it clears its environment and its
+ // environment is already cleared.
+ // before checking for cleared environment
+ // check, if member <mpVertPosOrientFrame> is set.
+ if ( GetVertPosOrientFrame() &&
+ ClearedEnvironment() && HasClearedEnvironment() )
+ {
+ return;
+ }
+
+ // use new class to position object
+ objectpositioning::SwToContentAnchoredObjectPosition
+ aObjPositioning( *GetVirtDrawObj() );
+ aObjPositioning.CalcPosition();
+
+ SetVertPosOrientFrame ( aObjPositioning.GetVertPosOrientFrame() );
+}
+
+// #i28701#
+bool SwFlyAtContentFrame::InvalidationAllowed( const InvalidationType _nInvalid ) const
+{
+ bool bAllowed( SwFlyFreeFrame::InvalidationAllowed( _nInvalid ) );
+
+ // forbiddance of base instance can't be over ruled.
+ if ( bAllowed )
+ {
+ if ( _nInvalid == INVALID_POS ||
+ _nInvalid == INVALID_ALL )
+ {
+ bAllowed = InvalidationOfPosAllowed();
+ }
+ }
+
+ return bAllowed;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flyincnt.cxx b/sw/source/core/layout/flyincnt.cxx
new file mode 100644
index 000000000..2455f7a6d
--- /dev/null
+++ b/sw/source/core/layout/flyincnt.cxx
@@ -0,0 +1,285 @@
+/* -*- 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 <doc.hxx>
+#include <frmtool.hxx>
+#include <hints.hxx>
+#include <fmtornt.hxx>
+#include <rootfrm.hxx>
+#include <flyfrms.hxx>
+#include <dflyobj.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/deleter.hxx>
+
+SwFlyInContentFrame::SwFlyInContentFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch ) :
+ SwFlyFrame( pFormat, pSib, pAnch )
+{
+ m_bInCnt = true;
+ SwTwips nRel = pFormat->GetVertOrient().GetPos();
+ // OD 2004-05-27 #i26791# - member <aRelPos> moved to <SwAnchoredObject>
+ Point aRelPos;
+ if( pAnch && pAnch->IsVertical() )
+ aRelPos.setX(-nRel);
+ else
+ aRelPos.setY(nRel);
+ SetCurrRelPos( aRelPos );
+}
+
+void SwFlyInContentFrame::DestroyImpl()
+{
+ if ( !GetFormat()->GetDoc()->IsInDtor() && GetAnchorFrame() )
+ {
+ SwRect aTmp( GetObjRectWithSpaces() );
+ SwFlyInContentFrame::NotifyBackground( FindPageFrame(), aTmp, PrepareHint::FlyFrameLeave );
+ }
+
+ SwFlyFrame::DestroyImpl();
+}
+
+SwFlyInContentFrame::~SwFlyInContentFrame()
+{
+}
+
+// #i28701#
+
+void SwFlyInContentFrame::SetRefPoint( const Point& rPoint,
+ const Point& rRelAttr,
+ const Point& rRelPos )
+{
+ // OD 2004-05-27 #i26791# - member <aRelPos> moved to <SwAnchoredObject>
+ OSL_ENSURE( rPoint != m_aRef || rRelAttr != GetCurrRelPos(), "SetRefPoint: no change" );
+ std::unique_ptr<SwFlyNotify, o3tl::default_delete<SwFlyNotify>> xNotify;
+ // No notify at a locked fly frame, if a fly frame is locked, there's
+ // already a SwFlyNotify object on the stack (MakeAll).
+ if( !IsLocked() )
+ xNotify.reset(new SwFlyNotify( this ));
+ m_aRef = rPoint;
+ SetCurrRelPos( rRelAttr );
+ SwRectFnSet aRectFnSet(GetAnchorFrame());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetPos( aFrm, rPoint + rRelPos );
+ }
+
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+ if (xNotify)
+ {
+ InvalidatePage();
+ setFrameAreaPositionValid(false);
+ m_bInvalid = true;
+ Calc(getRootFrame()->GetCurrShell()->GetOut());
+ xNotify.reset();
+ }
+}
+
+void SwFlyInContentFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ std::pair<std::unique_ptr<SwAttrSetChg>, std::unique_ptr<SwAttrSetChg>> aTweakedChgs;
+ std::pair<const SfxPoolItem*, const SfxPoolItem*> aSuperArgs(nullptr, nullptr);
+ switch(pLegacy->GetWhich())
+ {
+ case RES_ATTRSET_CHG:
+ {
+ auto pOldAttrSetChg = static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto pNewAttrSetChg = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ if(pOldAttrSetChg
+ && pNewAttrSetChg
+ && ((SfxItemState::SET == pNewAttrSetChg->GetChgSet()->GetItemState(RES_SURROUND, false))
+ || (SfxItemState::SET == pNewAttrSetChg->GetChgSet()->GetItemState(RES_FRMMACRO, false))))
+ {
+ aTweakedChgs.second = std::make_unique<SwAttrSetChg>(*pOldAttrSetChg);
+ aTweakedChgs.second->ClearItem(RES_SURROUND);
+ aTweakedChgs.second->ClearItem(RES_FRMMACRO);
+ if(aTweakedChgs.second->Count())
+ {
+ aTweakedChgs.first = std::make_unique<SwAttrSetChg>(*pOldAttrSetChg);
+ aTweakedChgs.first->ClearItem(RES_SURROUND);
+ aTweakedChgs.first->ClearItem(RES_FRMMACRO);
+ aSuperArgs = std::pair<const SfxPoolItem*, const SfxPoolItem*>(aTweakedChgs.first.get(), aTweakedChgs.second.get());
+ }
+ } else if (pNewAttrSetChg && pNewAttrSetChg->GetChgSet()->Count())
+ aSuperArgs = std::pair<const SfxPoolItem*, const SfxPoolItem*>(pLegacy->m_pOld, pLegacy->m_pNew);
+ break;
+ }
+ case RES_SURROUND:
+ case RES_FRMMACRO:
+ break;
+ default:
+ aSuperArgs = std::pair<const SfxPoolItem*, const SfxPoolItem*>(pLegacy->m_pOld, pLegacy->m_pNew);
+ }
+ if(aSuperArgs.second)
+ {
+ SwFlyFrame::SwClientNotify(rMod, sw::LegacyModifyHint(aSuperArgs.first, aSuperArgs.second));
+ if(GetAnchorFrame())
+ AnchorFrame()->Prepare(PrepareHint::FlyFrameAttributesChanged, GetFormat());
+ }
+}
+
+/// Here the content gets formatted initially.
+void SwFlyInContentFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs )
+{
+ if ( !getFrameArea().Height() )
+ {
+ Lock(); //don't format the anchor on the crook.
+ SwContentFrame *pContent = ContainsContent();
+ while ( pContent )
+ { pContent->Calc(pRenderContext);
+ pContent = pContent->GetNextContentFrame();
+ }
+ Unlock();
+ }
+ SwFlyFrame::Format( pRenderContext, pAttrs );
+}
+
+/** Calculate object position
+ *
+ * @note: In contrast to other Frames, we only calculate the relative position
+ * here. The absolute position is only calculated using SetAbsPos.
+ **/
+void SwFlyInContentFrame::MakeObjPos()
+{
+ if ( isFrameAreaPositionValid() )
+ return;
+
+ setFrameAreaPositionValid(true);
+ SwFlyFrameFormat *pFormat = GetFormat();
+ const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
+ //Update the current values in the format if needed, during this we of
+ //course must not send any Modify.
+ const bool bVert = GetAnchorFrame()->IsVertical();
+ SwTwips nOld = rVert.GetPos();
+ SwTwips nAct = bVert ? -GetCurrRelPos().X() : GetCurrRelPos().Y();
+ if( nAct != nOld )
+ {
+ SwFormatVertOrient aVert( rVert );
+ aVert.SetPos( nAct );
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aVert );
+ pFormat->UnlockModify();
+ }
+}
+
+void SwFlyInContentFrame::ActionOnInvalidation( const InvalidationType _nInvalid )
+{
+ if ( INVALID_POS == _nInvalid || INVALID_ALL == _nInvalid )
+ AnchorFrame()->Prepare( PrepareHint::FlyFrameAttributesChanged, &GetFrameFormat() );
+}
+
+void SwFlyInContentFrame::NotifyBackground( SwPageFrame *, const SwRect& rRect,
+ PrepareHint eHint)
+{
+ if ( eHint == PrepareHint::FlyFrameAttributesChanged )
+ AnchorFrame()->Prepare( PrepareHint::FlyFrameAttributesChanged );
+ else
+ AnchorFrame()->Prepare( eHint, static_cast<void const *>(&rRect) );
+}
+
+Point const & SwFlyInContentFrame::GetRelPos() const
+{
+ Calc(getRootFrame()->GetCurrShell()->GetOut());
+ return GetCurrRelPos();
+}
+
+/// @see SwRowFrame::RegistFlys()
+void SwFlyInContentFrame::RegistFlys()
+{
+ SwPageFrame *pPage = FindPageFrame();
+ OSL_ENSURE( pPage, "Register Flys without pages?" );
+ ::RegistFlys( pPage, this );
+}
+
+void SwFlyInContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
+{
+ // OD 2004-01-19 #110582#
+ if ( !GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetVirtDrawObj()->GetLayer() ) )
+ {
+ return;
+ }
+
+ if ( !GetAnchorFrame() || IsLocked() || IsColLocked() || !FindPageFrame() )
+ return;
+
+ Lock(); // The curtain falls
+
+ //does the notification in the DTor
+ const SwFlyNotify aNotify( this );
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ if ( IsClipped() )
+ {
+ setFrameAreaSizeValid(false);
+ m_bHeightClipped = m_bWidthClipped = false;
+ }
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() || !m_bValidContentPos )
+ {
+ //Only stop, if the flag is set!!
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFramePrintAreaValid(false);
+ }
+
+ if ( !isFramePrintAreaValid() )
+ {
+ MakePrtArea( rAttrs );
+ m_bValidContentPos = false;
+ }
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ Format( getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
+ }
+
+ if ( !isFrameAreaPositionValid() )
+ {
+ MakeObjPos();
+ }
+
+ if ( !m_bValidContentPos )
+ MakeContentPos( rAttrs );
+
+ // re-activate clipping of as-character anchored Writer fly frames
+ // depending on compatibility option <ClipAsCharacterAnchoredWriterFlyFrames>
+ if ( isFrameAreaPositionValid() &&
+ isFrameAreaSizeValid() &&
+ GetFormat()->getIDocumentSettingAccess().get( DocumentSettingId::CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAME ) )
+ {
+ SwFrame* pFrame = AnchorFrame();
+ if ( getFrameArea().Left() == (pFrame->getFrameArea().Left()+pFrame->getFramePrintArea().Left()) &&
+ getFrameArea().Width() > pFrame->getFramePrintArea().Width() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( pFrame->getFramePrintArea().Width() );
+ setFramePrintAreaValid(false);
+ m_bWidthClipped = true;
+ }
+ }
+ }
+ Unlock();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flylay.cxx b/sw/source/core/layout/flylay.cxx
new file mode 100644
index 000000000..209ca1fcf
--- /dev/null
+++ b/sw/source/core/layout/flylay.cxx
@@ -0,0 +1,1505 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <cntfrm.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <ftnfrm.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <sectfrm.hxx>
+#include <notxtfrm.hxx>
+#include <txtfly.hxx>
+
+#include <svx/svdpage.hxx>
+#include <editeng/ulspitem.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <ndole.hxx>
+#include <tabfrm.hxx>
+#include <flyfrms.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <environmentofanchoredobject.hxx>
+#include <sortedobjs.hxx>
+#include <viewimp.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <pam.hxx>
+#include <ndindex.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+SwFlyFreeFrame::SwFlyFreeFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch )
+: SwFlyFrame( pFormat, pSib, pAnch ),
+ // #i34753#
+ mbNoMakePos( false ),
+ // #i37068#
+ mbNoMoveOnCheckClip( false )
+{
+}
+
+void SwFlyFreeFrame::DestroyImpl()
+{
+ // #i28701# - use new method <GetPageFrame()>
+ if( GetPageFrame() )
+ {
+ if( GetFormat()->GetDoc()->IsInDtor() )
+ {
+ // #i29879# - remove also to-frame anchored Writer
+ // fly frame from page.
+ const bool bRemoveFromPage =
+ GetPageFrame()->GetSortedObjs() &&
+ ( IsFlyAtContentFrame() ||
+ ( GetAnchorFrame() && GetAnchorFrame()->IsFlyFrame() ) );
+ if ( bRemoveFromPage )
+ {
+ GetPageFrame()->GetSortedObjs()->Remove( *this );
+ }
+ }
+ else
+ {
+ SwRect aTmp( GetObjRectWithSpaces() );
+ SwFlyFreeFrame::NotifyBackground( GetPageFrame(), aTmp, PrepareHint::FlyFrameLeave );
+ }
+ }
+
+ SwFlyFrame::DestroyImpl();
+}
+
+SwFlyFreeFrame::~SwFlyFreeFrame()
+{
+#if 0
+ // we are possibly in ContourCache, make sure we vanish
+ ::ClrContourCache(GetVirtDrawObj());
+#endif
+}
+
+// #i28701#
+/** Notifies the background (all ContentFrames that currently are overlapping).
+ *
+ * Additionally, the window is also directly invalidated (especially where
+ * there are no overlapping ContentFrames).
+ * This also takes ContentFrames within other Flys into account.
+ */
+void SwFlyFreeFrame::NotifyBackground( SwPageFrame *pPageFrame,
+ const SwRect& rRect, PrepareHint eHint )
+{
+ ::Notify_Background( GetVirtDrawObj(), pPageFrame, rRect, eHint, true );
+}
+
+void SwFlyFreeFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
+{
+ if ( !GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetVirtDrawObj()->GetLayer() ) )
+ {
+ return;
+ }
+
+ if ( !GetAnchorFrame() || IsLocked() || IsColLocked() )
+ {
+ return;
+ }
+
+ // #i28701# - use new method <GetPageFrame()>
+ if( !GetPageFrame() && GetAnchorFrame()->IsInFly() )
+ {
+ SwFlyFrame* pFly = AnchorFrame()->FindFlyFrame();
+ SwPageFrame *pPageFrame = pFly ? pFly->FindPageFrame() : nullptr;
+ if( pPageFrame )
+ pPageFrame->AppendFlyToPage( this );
+ }
+
+ if( !GetPageFrame() )
+ {
+ return;
+ }
+
+ Lock(); // The curtain drops
+
+ // takes care of the notification in the dtor
+ const SwFlyNotify aNotify( this );
+
+ if ( IsClipped() )
+ {
+ setFrameAreaSizeValid(false);
+ m_bHeightClipped = m_bWidthClipped = false;
+ // no invalidation of position,
+ // if anchored object is anchored inside a Writer fly frame,
+ // its position is already locked, and it follows the text flow.
+ // #i34753# - add condition:
+ // no invalidation of position, if no direct move is requested in <CheckClip(..)>
+ if ( !IsNoMoveOnCheckClip() &&
+ !( PositionLocked() &&
+ GetAnchorFrame()->IsInFly() &&
+ GetFrameFormat().GetFollowTextFlow().GetValue() ) )
+ {
+ setFrameAreaPositionValid(false);
+ }
+ }
+
+ // #i81146# new loop control
+ int nLoopControlRuns = 0;
+ const int nLoopControlMax = 10;
+
+ // RotateFlyFrame3 - outer frame
+ const double fRotation(getLocalFrameRotation());
+ const bool bRotated(!basegfx::fTools::equalZero(fRotation));
+
+ if(bRotated)
+ {
+ // Re-layout may be partially (see all isFrameAreaDefinitionValid() flags),
+ // so resetting the local SwFrame(s) in the local SwFrameAreaDefinition is
+ // needed. Reset to BoundAreas will be done below automatically
+ if(isTransformableSwFrame())
+ {
+ getTransformableSwFrame()->restoreFrameAreas();
+ }
+ }
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() || m_bFormatHeightOnly || !m_bValidContentPos )
+ {
+ SwRectFnSet aRectFnSet(this);
+ const SwFormatFrameSize *pSz;
+ { // Additional scope, so aAccess will be destroyed before the check!
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ pSz = &rAttrs.GetAttrSet().GetFrameSize();
+
+ // Only set when the flag is set!
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFramePrintAreaValid(false);
+ }
+
+ if ( !isFramePrintAreaValid() )
+ {
+ MakePrtArea( rAttrs );
+ m_bValidContentPos = false;
+ }
+
+ if ( !isFrameAreaSizeValid() || m_bFormatHeightOnly )
+ {
+ setFrameAreaSizeValid(false);
+ Format( getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
+ m_bFormatHeightOnly = false;
+ }
+ }
+
+ if ( !isFrameAreaPositionValid() )
+ {
+ const Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
+ // #i26791# - use new method <MakeObjPos()>
+ // #i34753# - no positioning, if requested.
+ if ( IsNoMakePos() )
+ {
+ setFrameAreaPositionValid(true);
+ }
+ else
+ // #i26791# - use new method <MakeObjPos()>
+ MakeObjPos();
+ if( aOldPos == aRectFnSet.GetPos(getFrameArea()) )
+ {
+ if( !isFrameAreaPositionValid() && GetAnchorFrame()->IsInSct() &&
+ !GetAnchorFrame()->FindSctFrame()->isFrameAreaDefinitionValid() )
+ {
+ setFrameAreaPositionValid(true);
+ }
+ }
+ else
+ {
+ setFrameAreaSizeValid(false);
+ }
+ }
+
+ if ( !m_bValidContentPos )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ MakeContentPos( rAttrs );
+ }
+
+ if ( isFrameAreaPositionValid() && isFrameAreaSizeValid() )
+ {
+ ++nLoopControlRuns;
+
+ OSL_ENSURE( nLoopControlRuns < nLoopControlMax, "LoopControl in SwFlyFreeFrame::MakeAll" );
+
+ if ( nLoopControlRuns < nLoopControlMax )
+ CheckClip( *pSz );
+ }
+ else
+ nLoopControlRuns = 0;
+ }
+
+ // RotateFlyFrame3 - outer frame
+ // Do not refresh transforms/Areas self here, this will be done
+ // when inner and outer frame are layouted, in SwNoTextFrame::MakeAll
+ if(bRotated)
+ {
+ // RotateFlyFrame3: Safe changes locally
+ // get center from outer frame (layout frame) to be on the safe side
+ const Point aCenter(getFrameArea().Center());
+ const basegfx::B2DPoint aB2DCenter(aCenter.X(), aCenter.Y());
+
+ if(!mpTransformableSwFrame)
+ {
+ mpTransformableSwFrame.reset(new TransformableSwFrame(*this));
+ }
+
+ getTransformableSwFrame()->createFrameAreaTransformations(
+ fRotation,
+ aB2DCenter);
+ getTransformableSwFrame()->adaptFrameAreasToTransformations();
+ }
+ else
+ {
+ // RotateFlyFrame3: Also need to clear ContourCache (if used),
+ // usually done in SwFlyFrame::NotifyDrawObj, but there relies on
+ // being in transform mode which is already reset then
+ if(isTransformableSwFrame())
+ {
+ ::ClrContourCache(GetVirtDrawObj());
+ }
+
+ // reset transformations to show that they are not used
+ mpTransformableSwFrame.reset();
+ }
+
+ Unlock();
+
+#if OSL_DEBUG_LEVEL > 0
+ SwRectFnSet aRectFnSet(this);
+ OSL_ENSURE( m_bHeightClipped || ( aRectFnSet.GetHeight(getFrameArea()) > 0 &&
+ aRectFnSet.GetHeight(getFramePrintArea()) > 0),
+ "SwFlyFreeFrame::Format(), flipping Fly." );
+
+#endif
+}
+
+bool SwFlyFreeFrame::supportsAutoContour() const
+{
+ static bool bOverrideHandleContourToAlwaysOff(true); // loplugin:constvars:ignore
+
+ // RotateFlyFrameFix: For LO6.0 we need to deactivate the AutoContour feature again, it is simply
+ // not clear how/if to use and save/load it in ODF. This has to be discussed.
+ // The reason not to remove is that this may be used as-is now, using a new switch.
+ // Even when not, the detection if it is possible will be needed in any case later.
+ if(bOverrideHandleContourToAlwaysOff)
+ {
+ return false;
+ }
+
+ if(!isTransformableSwFrame())
+ {
+ // support only when transformed, else there is no free space
+ return false;
+ }
+
+ // Check for Borders. If we have Borders, do (currently) not support,
+ // since borders do not transform with the object.
+ // (Will need to be enhanced to take into account if we have Borders and if these
+ // transform with the object)
+ SwBorderAttrAccess aAccess(SwFrame::GetCache(), this);
+ const SwBorderAttrs &rAttrs(*aAccess.Get());
+
+ if(rAttrs.IsLine())
+ {
+ return false;
+ }
+
+ // Check for Padding. Do not support when padding is used, this will
+ // produce a covered space around the object (filled with fill defines)
+ const SvxBoxItem* pBoxItem(nullptr);
+
+ if(GetFormat() && (pBoxItem = GetFormat()->GetItemIfSet(RES_BOX, false)))
+ {
+ if(pBoxItem->HasBorder(/*bTreatPaddingAsBorder*/true))
+ {
+ return false;
+ }
+ }
+
+ // check for Fill - if we have fill, it will fill the gaps and we will not
+ // support AutoContour
+ if(GetFormat() && GetFormat()->supportsFullDrawingLayerFillAttributeSet())
+ {
+ const drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(GetFormat()->getSdrAllFillAttributesHelper());
+
+ if(aFillAttributes && aFillAttributes->isUsed())
+ {
+ return false;
+ }
+ }
+ else
+ {
+ const std::unique_ptr<SvxBrushItem> aBack(GetFormat()->makeBackgroundBrushItem());
+
+ if(aBack->isUsed())
+ {
+ return false;
+ }
+ }
+
+ // else, support
+ return true;
+}
+
+// RotateFlyFrame3 - Support for Transformations - outer frame
+basegfx::B2DHomMatrix SwFlyFreeFrame::getFrameAreaTransformation() const
+{
+ if(isTransformableSwFrame())
+ {
+ // use pre-created transformation
+ return getTransformableSwFrame()->getLocalFrameAreaTransformation();
+ }
+
+ // call parent
+ return SwFlyFrame::getFrameAreaTransformation();
+}
+
+basegfx::B2DHomMatrix SwFlyFreeFrame::getFramePrintAreaTransformation() const
+{
+ if(isTransformableSwFrame())
+ {
+ // use pre-created transformation
+ return getTransformableSwFrame()->getLocalFramePrintAreaTransformation();
+ }
+
+ // call parent
+ return SwFlyFrame::getFramePrintAreaTransformation();
+}
+
+// RotateFlyFrame3 - Support for Transformations
+void SwFlyFreeFrame::transform_translate(const Point& rOffset)
+{
+ // call parent - this will do the basic transform for SwRect(s)
+ // in the SwFrameAreaDefinition
+ SwFlyFrame::transform_translate(rOffset);
+
+ // check if the Transformations need to be adapted
+ if(isTransformableSwFrame())
+ {
+ const basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ rOffset.X(), rOffset.Y()));
+
+ // transform using TransformableSwFrame
+ getTransformableSwFrame()->transform(aTransform);
+ }
+}
+
+// RotateFlyFrame3 - outer frame
+double getLocalFrameRotation_from_SwNoTextFrame(const SwNoTextFrame& rNoTextFrame)
+{
+ return rNoTextFrame.getLocalFrameRotation();
+}
+
+double SwFlyFreeFrame::getLocalFrameRotation() const
+{
+ // SwLayoutFrame::Lower() != SwFrame::GetLower(), but SwFrame::GetLower()
+ // calls SwLayoutFrame::Lower() when it's a SwLayoutFrame - so use GetLower()
+ const SwNoTextFrame* pSwNoTextFrame(dynamic_cast< const SwNoTextFrame* >(GetLower()));
+
+ if(nullptr != pSwNoTextFrame)
+ {
+ return getLocalFrameRotation_from_SwNoTextFrame(*pSwNoTextFrame);
+ }
+
+ // no rotation
+ return 0.0;
+}
+
+/** determines, if direct environment of fly frame has 'auto' size
+
+ #i17297#
+ start with anchor frame and search via <GetUpper()> for a header, footer,
+ row or fly frame stopping at page frame.
+ return <true>, if such a frame is found and it has 'auto' size.
+ otherwise <false> is returned.
+
+ @return boolean indicating, that direct environment has 'auto' size
+*/
+bool SwFlyFreeFrame::HasEnvironmentAutoSize() const
+{
+ bool bRetVal = false;
+
+ const SwFrame* pToBeCheckedFrame = GetAnchorFrame();
+ while ( pToBeCheckedFrame &&
+ !pToBeCheckedFrame->IsPageFrame() )
+ {
+ if ( pToBeCheckedFrame->IsHeaderFrame() ||
+ pToBeCheckedFrame->IsFooterFrame() ||
+ pToBeCheckedFrame->IsRowFrame() ||
+ pToBeCheckedFrame->IsFlyFrame() )
+ {
+ bRetVal = SwFrameSize::Fixed !=
+ pToBeCheckedFrame->GetAttrSet()->GetFrameSize().GetHeightSizeType();
+ break;
+ }
+ else
+ {
+ pToBeCheckedFrame = pToBeCheckedFrame->GetUpper();
+ }
+ }
+
+ return bRetVal;
+}
+
+void SwFlyFreeFrame::CheckClip( const SwFormatFrameSize &rSz )
+{
+ // It's probably time now to take appropriate measures, if the Fly
+ // doesn't fit into its surrounding.
+ // First, the Fly gives up its position, then it's formatted.
+ // Only if it still doesn't fit after giving up its position, the
+ // width or height are given up as well. The frame will be squeezed
+ // as much as needed.
+
+ const SwVirtFlyDrawObj *pObj = GetVirtDrawObj();
+ SwRect aClip, aTmpStretch;
+ ::CalcClipRect( pObj, aClip );
+ ::CalcClipRect( pObj, aTmpStretch, false );
+ aClip.Intersection_( aTmpStretch );
+
+ const tools::Long nBot = getFrameArea().Top() + getFrameArea().Height();
+ const tools::Long nRig = getFrameArea().Left() + getFrameArea().Width();
+ const tools::Long nClipBot = aClip.Top() + aClip.Height();
+ const tools::Long nClipRig = aClip.Left() + aClip.Width();
+
+ const bool bBot = nBot > nClipBot;
+ const bool bRig = nRig > nClipRig;
+ if (( bBot || bRig ) && !IsDraggingOffPageAllowed(FindFrameFormat(GetDrawObj())))
+ {
+ bool bAgain = false;
+ // #i37068# - no move, if it's requested
+ if ( bBot && !IsNoMoveOnCheckClip() &&
+ !GetDrawObjs() && !GetAnchorFrame()->IsInTab() )
+ {
+ SwFrame* pHeader = FindFooterOrHeader();
+ // In a header, correction of the position is no good idea.
+ // If the fly moves, some paragraphs have to be formatted, this
+ // could cause a change of the height of the headerframe,
+ // now the flyframe can change its position and so on ...
+ if ( !pHeader || !pHeader->IsHeaderFrame() )
+ {
+ const tools::Long nOld = getFrameArea().Top();
+
+ // tdf#112443 disable positioning if content is completely off page
+ bool bDisableOffPagePositioning = GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING);
+ if ( !bDisableOffPagePositioning || nOld <= nClipBot)
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setY( std::max( aClip.Top(), nClipBot - aFrm.Height() ) );
+ }
+
+ if ( getFrameArea().Top() != nOld )
+ {
+ bAgain = true;
+ }
+
+ m_bHeightClipped = true;
+ }
+ }
+ if ( bRig )
+ {
+ const tools::Long nOld = getFrameArea().Left();
+
+ // tdf#112443 disable positioning if content is completely off page
+ bool bDisableOffPagePositioning = GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING);
+ if ( !bDisableOffPagePositioning || nOld <= nClipRig )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX( std::max( aClip.Left(), nClipRig - aFrm.Width() ) );
+ }
+
+ if ( getFrameArea().Left() != nOld )
+ {
+ const SwFormatHoriOrient &rH = GetFormat()->GetHoriOrient();
+ // Left-aligned ones may not be moved to the left when they
+ // are avoiding another one.
+ if( rH.GetHoriOrient() == text::HoriOrientation::LEFT )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX( nOld );
+ }
+ else
+ {
+ bAgain = true;
+ }
+ }
+ m_bWidthClipped = true;
+ }
+ if ( bAgain )
+ {
+ setFrameAreaSizeValid(false);
+ }
+ else
+ {
+ // If we reach this branch, the Frame protrudes into forbidden
+ // areas, and correcting the position is not allowed or not
+ // possible or not required.
+
+ // For Flys with OLE objects as lower, we make sure that
+ // we always resize proportionally
+ Size aOldSize( getFrameArea().SSize() );
+
+ // First, setup the FrameRect, then transfer it to the Frame.
+ SwRect aFrameRect( getFrameArea() );
+
+ if ( bBot )
+ {
+ tools::Long nDiff = nClipBot;
+ nDiff -= aFrameRect.Top(); // nDiff represents the available distance
+ nDiff = aFrameRect.Height() - nDiff;
+ aFrameRect.Height( aFrameRect.Height() - nDiff );
+ m_bHeightClipped = true;
+ }
+ if ( bRig )
+ {
+ tools::Long nDiff = nClipRig;
+ nDiff -= aFrameRect.Left();// nDiff represents the available distance
+ nDiff = aFrameRect.Width() - nDiff;
+ aFrameRect.Width( aFrameRect.Width() - nDiff );
+ m_bWidthClipped = true;
+ }
+
+ // #i17297# - no proportional
+ // scaling of graphics in environments, which determines its size
+ // by its content ('auto' size). Otherwise layout loops can occur and
+ // layout sizes of the environment can be incorrect.
+ // Such environment are:
+ // (1) header and footer frames with 'auto' size
+ // (2) table row frames with 'auto' size
+ // (3) fly frames with 'auto' size
+ // Note: section frames seems to be not critical - didn't found
+ // any critical layout situation so far.
+ if ( Lower() && Lower()->IsNoTextFrame() &&
+ (static_cast<SwNoTextFrame*>(Lower())->GetNode()->GetOLENode() ||
+ !HasEnvironmentAutoSize() ) )
+ {
+ // If width and height got adjusted, then the bigger
+ // change is relevant.
+ if ( aFrameRect.Width() != aOldSize.Width() &&
+ aFrameRect.Height()!= aOldSize.Height() )
+ {
+ if ( (aOldSize.Width() - aFrameRect.Width()) >
+ (aOldSize.Height()- aFrameRect.Height()) )
+ aFrameRect.Height( aOldSize.Height() );
+ else
+ aFrameRect.Width( aOldSize.Width() );
+ }
+
+ // Adjusted the width? change height proportionally
+ if( aFrameRect.Width() != aOldSize.Width() )
+ {
+ aFrameRect.Height( aFrameRect.Width() * aOldSize.Height() /
+ aOldSize.Width() );
+ m_bHeightClipped = true;
+ }
+ // Adjusted the height? change width proportionally
+ else if( aFrameRect.Height() != aOldSize.Height() )
+ {
+ aFrameRect.Width( aFrameRect.Height() * aOldSize.Width() /
+ aOldSize.Height() );
+ m_bWidthClipped = true;
+ }
+
+ // #i17297# - reactivate change
+ // of size attribute for fly frames containing an ole object.
+
+ // Added the aFrameRect.HasArea() hack, because
+ // the environment of the ole object does not have to be valid
+ // at this moment, or even worse, it does not have to have a
+ // reasonable size. In this case we do not want to change to
+ // attributes permanently. Maybe one day somebody dares to remove
+ // this code.
+ if ( aFrameRect.HasArea() &&
+ static_cast<SwNoTextFrame*>(Lower())->GetNode()->GetOLENode() &&
+ ( m_bWidthClipped || m_bHeightClipped ) )
+ {
+ SwFlyFrameFormat *pFormat = GetFormat();
+ pFormat->LockModify();
+ SwFormatFrameSize aFrameSize( rSz );
+ aFrameSize.SetWidth( aFrameRect.Width() );
+ aFrameSize.SetHeight( aFrameRect.Height() );
+ pFormat->SetFormatAttr( aFrameSize );
+ pFormat->UnlockModify();
+ }
+ }
+
+ // Now change the Frame; for columns, we put the new values into the attributes,
+ // otherwise we'll end up with unwanted side-effects/oscillations
+ const tools::Long nPrtHeightDiff = getFrameArea().Height() - getFramePrintArea().Height();
+ const tools::Long nPrtWidthDiff = getFrameArea().Width() - getFramePrintArea().Width();
+ maUnclippedFrame = getFrameArea();
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( aFrameRect.Height() );
+ aFrm.Width ( std::max( tools::Long(MINLAY), aFrameRect.Width() ) );
+ }
+
+ if ( Lower() && Lower()->IsColumnFrame() )
+ {
+ ColLock(); //lock grow/shrink
+ const Size aTmpOldSize( getFramePrintArea().SSize() );
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( getFrameArea().Height() - nPrtHeightDiff );
+ aPrt.Width ( getFrameArea().Width() - nPrtWidthDiff );
+ }
+
+ ChgLowersProp( aTmpOldSize );
+ SwFrame *pLow = Lower();
+ do
+ {
+ pLow->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // also calculate the (Column)BodyFrame
+ static_cast<SwLayoutFrame*>(pLow)->Lower()->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ pLow = pLow->GetNext();
+ } while ( pLow );
+ ::CalcContent( this );
+ ColUnlock();
+
+ if ( !isFrameAreaSizeValid() && !m_bWidthClipped )
+ {
+ setFrameAreaSizeValid(true);
+ m_bFormatHeightOnly = true;
+ }
+ }
+ else
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( getFrameArea().Height() - nPrtHeightDiff );
+ aPrt.Width ( getFrameArea().Width() - nPrtWidthDiff );
+ }
+ }
+ }
+
+ // #i26945#
+ OSL_ENSURE( getFrameArea().Height() >= 0,
+ "<SwFlyFreeFrame::CheckClip(..)> - fly frame has negative height now." );
+}
+
+/** method to determine, if a <MakeAll()> on the Writer fly frame is possible
+ #i43771#
+*/
+bool SwFlyFreeFrame::IsFormatPossible() const
+{
+ return SwFlyFrame::IsFormatPossible() &&
+ ( GetPageFrame() ||
+ ( GetAnchorFrame() && GetAnchorFrame()->IsInFly() ) );
+}
+
+SwFlyLayFrame::SwFlyLayFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch ) :
+ SwFlyFreeFrame( pFormat, pSib, pAnch )
+{
+ m_bLayout = true;
+}
+
+void SwFlyLayFrame::RegisterAtPage(SwPageFrame & rPageFrame)
+{
+ assert(GetPageFrame() != &rPageFrame);
+ if (GetPageFrame())
+ {
+ GetPageFrame()->MoveFly( this, &rPageFrame );
+ }
+ else
+ {
+ rPageFrame.AppendFlyToPage( this );
+ }
+}
+
+// #i28701#
+
+void SwFlyLayFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(!pLegacy->m_pNew)
+ return;
+ const auto pAnch = GetAnchorFromPoolItem(*pLegacy->m_pNew);
+
+ if(!pAnch)
+ {
+ SwFlyFrame::SwClientNotify(rMod, rHint);
+ return;
+ }
+ SAL_WARN_IF(pAnch->GetAnchorId() == GetFormat()->GetAnchor().GetAnchorId(), "sw.core", "Invalid change of anchor type.");
+
+ // Unregister, get hold of the page, attach to the corresponding LayoutFrame.
+ SwRect aOld(GetObjRectWithSpaces());
+ // #i28701# - use new method <GetPageFrame()>
+ SwPageFrame* pOldPage = GetPageFrame();
+ AnchorFrame()->RemoveFly(this);
+
+ if(RndStdIds::FLY_AT_PAGE == pAnch->GetAnchorId())
+ {
+ SwRootFrame* pRoot = getRootFrame();
+ SwPageFrame* pTmpPage = static_cast<SwPageFrame*>(pRoot->Lower());
+ sal_uInt16 nPagesToFlip = pAnch->GetPageNum()-1;
+ while(pTmpPage && nPagesToFlip)
+ {
+ pTmpPage = static_cast<SwPageFrame*>(pTmpPage->GetNext());
+ --nPagesToFlip;
+ }
+ if(pTmpPage && !nPagesToFlip)
+ {
+ // #i50432# - adjust synopsis of <PlaceFly(..)>
+ pTmpPage->PlaceFly(this, nullptr);
+ }
+ if(!pTmpPage)
+ {
+ pRoot->SetAssertFlyPages();
+ pRoot->AssertFlyPages();
+ }
+ }
+ else
+ {
+ SwNodeIndex aIdx(pAnch->GetContentAnchor()->nNode);
+ SwContentFrame* pContent = GetFormat()->GetDoc()->GetNodes().GoNext(&aIdx)->
+ GetContentNode()->getLayoutFrame(getRootFrame(), nullptr, nullptr);
+ if(pContent)
+ {
+ SwFlyFrame *pTmp = pContent->FindFlyFrame();
+ if(pTmp)
+ pTmp->AppendFly(this);
+ }
+ }
+ // #i28701# - use new method <GetPageFrame()>
+ if ( pOldPage && pOldPage != GetPageFrame() )
+ NotifyBackground( pOldPage, aOld, PrepareHint::FlyFrameLeave );
+ SetCompletePaint();
+ InvalidateAll();
+ SetNotifyBack();
+}
+
+void SwPageFrame::AppendFlyToPage( SwFlyFrame *pNew )
+{
+ if ( !pNew->GetVirtDrawObj()->IsInserted() )
+ getRootFrame()->GetDrawPage()->InsertObject(
+ static_cast<SdrObject*>(pNew->GetVirtDrawObj()),
+ pNew->GetVirtDrawObj()->GetReferencedObj().GetOrdNumDirect() );
+
+ InvalidateSpelling();
+ InvalidateSmartTags();
+ InvalidateAutoCompleteWords();
+ InvalidateWordCount();
+
+ if ( GetUpper() )
+ {
+ static_cast<SwRootFrame*>(GetUpper())->SetIdleFlags();
+ static_cast<SwRootFrame*>(GetUpper())->InvalidateBrowseWidth();
+ }
+
+ SdrObject* pObj = pNew->GetVirtDrawObj();
+ OSL_ENSURE( pNew->GetAnchorFrame(), "Fly without Anchor" );
+ SwFlyFrame* pFly = const_cast<SwFlyFrame*>(pNew->GetAnchorFrame()->FindFlyFrame());
+ if ( pFly && pObj->GetOrdNum() < pFly->GetVirtDrawObj()->GetOrdNum() )
+ {
+ //#i119945# set pFly's OrdNum to _rNewObj's. So when pFly is removed by Undo, the original OrdNum will not be changed.
+ sal_uInt32 nNewNum = pObj->GetOrdNumDirect();
+ SdrObject* pDrawObj = nullptr;
+ if (auto pFormat = pFly->GetFormat())
+ if (auto pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_FLYFRMFMT))
+ pDrawObj = pShapeFormat->FindRealSdrObject();
+
+ if (pDrawObj)
+ {
+ if (auto pPage = pDrawObj->getSdrPageFromSdrObject())
+ pPage->SetObjectOrdNum(pDrawObj->GetOrdNumDirect(), nNewNum);
+ else
+ pDrawObj->SetOrdNum(nNewNum);
+ }
+
+ if ( pObj->getSdrPageFromSdrObject() )
+ pObj->getSdrPageFromSdrObject()->SetObjectOrdNum( pFly->GetVirtDrawObj()->GetOrdNumDirect(), nNewNum + (pDrawObj ? 1 : 0) );
+ else
+ pFly->GetVirtDrawObj()->SetOrdNum( nNewNum + (pDrawObj ? 1 : 0));
+ }
+
+ // Don't look further at Flys that sit inside the Content.
+ if ( pNew->IsFlyInContentFrame() )
+ InvalidateFlyInCnt();
+ else
+ {
+ InvalidateFlyContent();
+
+ if ( !m_pSortedObjs )
+ {
+ m_pSortedObjs.reset(new SwSortedObjs());
+ }
+
+ const bool bSuccessInserted = m_pSortedObjs->Insert( *pNew );
+ OSL_ENSURE( bSuccessInserted, "Fly not inserted in Sorted." );
+
+ // #i87493#
+ OSL_ENSURE( pNew->GetPageFrame() == nullptr || pNew->GetPageFrame() == this,
+ "<SwPageFrame::AppendFlyToPage(..)> - anchored fly frame seems to be registered at another page frame. Serious defect." );
+ // #i28701# - use new method <SetPageFrame(..)>
+ pNew->SetPageFrame( this );
+ pNew->InvalidatePage( this );
+ // #i28701#
+ pNew->UnlockPosition();
+ // needed to reposition at-page anchored flys moved from different page
+ pNew->InvalidateObjPos();
+
+ // Notify accessible layout. That's required at this place for
+ // frames only where the anchor is moved. Creation of new frames
+ // is additionally handled by the SwFrameNotify class.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( GetUpper() &&
+ static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() &&
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() )
+ {
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp()
+ ->AddAccessibleFrame( pNew );
+ }
+#endif
+ }
+
+ // #i28701# - correction: consider also drawing objects
+ if ( !pNew->GetDrawObjs() )
+ return;
+
+ SwSortedObjs &rObjs = *pNew->GetDrawObjs();
+ for (SwAnchoredObject* pTmpObj : rObjs)
+ {
+ if ( auto pTmpFly = pTmpObj->DynCastFlyFrame() )
+ {
+ // #i28701# - use new method <GetPageFrame()>
+ if ( pTmpFly->IsFlyFreeFrame() && !pTmpFly->GetPageFrame() )
+ AppendFlyToPage( pTmpFly );
+ }
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( pTmpObj) != nullptr )
+ {
+ // #i87493#
+ if ( pTmpObj->GetPageFrame() != this )
+ {
+ if ( pTmpObj->GetPageFrame() != nullptr )
+ {
+ pTmpObj->GetPageFrame()->RemoveDrawObjFromPage( *pTmpObj );
+ }
+ AppendDrawObjToPage( *pTmpObj );
+ }
+ }
+ }
+}
+
+void SwPageFrame::RemoveFlyFromPage( SwFlyFrame *pToRemove )
+{
+ const sal_uInt32 nOrdNum = pToRemove->GetVirtDrawObj()->GetOrdNum();
+ getRootFrame()->GetDrawPage()->RemoveObject( nOrdNum );
+ pToRemove->GetVirtDrawObj()->ReferencedObj().SetOrdNum( nOrdNum );
+
+ if ( GetUpper() )
+ {
+ if ( !pToRemove->IsFlyInContentFrame() )
+ static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
+ static_cast<SwRootFrame*>(GetUpper())->InvalidateBrowseWidth();
+ }
+
+ // Don't look further at Flys that sit inside the Content.
+ if ( pToRemove->IsFlyInContentFrame() )
+ return;
+
+ // Don't delete collections just yet. This will happen at the end of the
+ // action in the RemoveSuperfluous of the page, kicked off by a method of
+ // the same name in the root.
+ // The FlyColl might be gone already, because the page's dtor is being
+ // executed.
+ // Remove it _before_ disposing accessible frames to avoid accesses to
+ // the Frame from event handlers.
+ if (m_pSortedObjs)
+ {
+ m_pSortedObjs->Remove(*pToRemove);
+ if (!m_pSortedObjs->size())
+ {
+ m_pSortedObjs.reset();
+ }
+ }
+
+ // Notify accessible layout. That's required at this place for
+ // frames only where the anchor is moved. Creation of new frames
+ // is additionally handled by the SwFrameNotify class.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( GetUpper() &&
+ static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() &&
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() )
+ {
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp()
+ ->DisposeAccessibleFrame( pToRemove, true );
+ }
+#endif
+
+ // #i28701# - use new method <SetPageFrame(..)>
+ pToRemove->SetPageFrame( nullptr );
+}
+
+void SwPageFrame::MoveFly( SwFlyFrame *pToMove, SwPageFrame *pDest )
+{
+ // Invalidations
+ if ( GetUpper() )
+ {
+ static_cast<SwRootFrame*>(GetUpper())->SetIdleFlags();
+ if ( !pToMove->IsFlyInContentFrame() && pDest->GetPhyPageNum() < GetPhyPageNum() )
+ static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
+ }
+
+ pDest->InvalidateSpelling();
+ pDest->InvalidateSmartTags();
+ pDest->InvalidateAutoCompleteWords();
+ pDest->InvalidateWordCount();
+
+ if ( pToMove->IsFlyInContentFrame() )
+ {
+ pDest->InvalidateFlyInCnt();
+ return;
+ }
+
+ // Notify accessible layout. That's required at this place for
+ // frames only where the anchor is moved. Creation of new frames
+ // is additionally handled by the SwFrameNotify class.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( GetUpper() &&
+ static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() &&
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() )
+ {
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp()
+ ->DisposeAccessibleFrame( pToMove, true );
+ }
+#endif
+
+ // The FlyColl might be gone already, because the page's dtor is being executed.
+ if ( m_pSortedObjs )
+ {
+ m_pSortedObjs->Remove( *pToMove );
+ if ( !m_pSortedObjs->size() )
+ {
+ m_pSortedObjs.reset();
+ }
+ }
+
+ // Register
+ if ( !pDest->GetSortedObjs() )
+ pDest->m_pSortedObjs.reset(new SwSortedObjs());
+
+ const bool bSuccessInserted = pDest->GetSortedObjs()->Insert( *pToMove );
+ OSL_ENSURE( bSuccessInserted, "Fly not inserted in Sorted." );
+
+ // #i28701# - use new method <SetPageFrame(..)>
+ pToMove->SetPageFrame( pDest );
+ pToMove->InvalidatePage( pDest );
+ pToMove->SetNotifyBack();
+ pDest->InvalidateFlyContent();
+ // #i28701#
+ pToMove->UnlockPosition();
+
+ // Notify accessible layout. That's required at this place for
+ // frames only where the anchor is moved. Creation of new frames
+ // is additionally handled by the SwFrameNotify class.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( GetUpper() &&
+ static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() &&
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() )
+ {
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp()
+ ->AddAccessibleFrame( pToMove );
+ }
+#endif
+
+ // #i28701# - correction: move lowers of Writer fly frame
+ if ( !pToMove->GetDrawObjs() )
+ return;
+
+ SwSortedObjs &rObjs = *pToMove->GetDrawObjs();
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyFreeFrame() )
+ {
+ // #i28701# - use new method <GetPageFrame()>
+ SwPageFrame* pPageFrame = pFly->GetPageFrame();
+ if ( pPageFrame )
+ pPageFrame->MoveFly( pFly, pDest );
+ else
+ pDest->AppendFlyToPage( pFly );
+ }
+ }
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( pObj) != nullptr )
+ {
+ RemoveDrawObjFromPage( *pObj );
+ pDest->AppendDrawObjToPage( *pObj );
+ }
+ }
+}
+
+void SwPageFrame::AppendDrawObjToPage( SwAnchoredObject& _rNewObj )
+{
+ if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rNewObj) == nullptr )
+ {
+ OSL_FAIL( "SwPageFrame::AppendDrawObjToPage(..) - anchored object of unexpected type -> object not appended" );
+ return;
+ }
+
+ if ( GetUpper() )
+ {
+ static_cast<SwRootFrame*>(GetUpper())->InvalidateBrowseWidth();
+ }
+
+ assert(_rNewObj.GetAnchorFrame());
+ SwFlyFrame* pFlyFrame = const_cast<SwFlyFrame*>(_rNewObj.GetAnchorFrame()->FindFlyFrame());
+ if ( pFlyFrame &&
+ _rNewObj.GetDrawObj()->GetOrdNum() < pFlyFrame->GetVirtDrawObj()->GetOrdNum() )
+ {
+ //#i119945# set pFly's OrdNum to _rNewObj's. So when pFly is removed by Undo, the original OrdNum will not be changed.
+ sal_uInt32 nNewNum = _rNewObj.GetDrawObj()->GetOrdNumDirect();
+ if ( _rNewObj.GetDrawObj()->getSdrPageFromSdrObject() )
+ _rNewObj.DrawObj()->getSdrPageFromSdrObject()->SetObjectOrdNum( pFlyFrame->GetVirtDrawObj()->GetOrdNumDirect(), nNewNum );
+ else
+ pFlyFrame->GetVirtDrawObj()->SetOrdNum( nNewNum );
+ }
+
+ if ( RndStdIds::FLY_AS_CHAR == _rNewObj.GetFrameFormat().GetAnchor().GetAnchorId() )
+ {
+ return;
+ }
+
+ if ( !m_pSortedObjs )
+ {
+ m_pSortedObjs.reset(new SwSortedObjs());
+ }
+ if ( !m_pSortedObjs->Insert( _rNewObj ) )
+ {
+ OSL_ENSURE( m_pSortedObjs->Contains( _rNewObj ),
+ "Drawing object not appended into list <pSortedObjs>." );
+ }
+ // #i87493#
+ OSL_ENSURE( _rNewObj.GetPageFrame() == nullptr || _rNewObj.GetPageFrame() == this,
+ "<SwPageFrame::AppendDrawObjToPage(..)> - anchored draw object seems to be registered at another page frame. Serious defect." );
+ _rNewObj.SetPageFrame( this );
+
+ // invalidate page in order to force a reformat of object layout of the page.
+ InvalidateFlyLayout();
+}
+
+void SwPageFrame::RemoveDrawObjFromPage( SwAnchoredObject& _rToRemoveObj )
+{
+ if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rToRemoveObj) == nullptr )
+ {
+ OSL_FAIL( "SwPageFrame::RemoveDrawObjFromPage(..) - anchored object of unexpected type -> object not removed" );
+ return;
+ }
+
+ if ( m_pSortedObjs )
+ {
+ m_pSortedObjs->Remove( _rToRemoveObj );
+ if ( !m_pSortedObjs->size() )
+ {
+ m_pSortedObjs.reset();
+ }
+ if ( GetUpper() )
+ {
+ if (RndStdIds::FLY_AS_CHAR !=
+ _rToRemoveObj.GetFrameFormat().GetAnchor().GetAnchorId())
+ {
+ static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
+ InvalidatePage();
+ }
+ static_cast<SwRootFrame*>(GetUpper())->InvalidateBrowseWidth();
+ }
+ }
+ _rToRemoveObj.SetPageFrame( nullptr );
+}
+
+// #i50432# - adjust method description and synopsis.
+void SwPageFrame::PlaceFly( SwFlyFrame* pFly, SwFlyFrameFormat* pFormat )
+{
+ // #i50432# - consider the case that page is an empty page:
+ // In this case append the fly frame at the next page
+ OSL_ENSURE( !IsEmptyPage() || GetNext(),
+ "<SwPageFrame::PlaceFly(..)> - empty page with no next page! -> fly frame appended at empty page" );
+ if ( IsEmptyPage() && GetNext() )
+ {
+ static_cast<SwPageFrame*>(GetNext())->PlaceFly( pFly, pFormat );
+ }
+ else
+ {
+ // If we received a Fly, we use that one. Otherwise, create a new
+ // one using the Format.
+ if ( pFly )
+ AppendFly( pFly );
+ else
+ {
+ OSL_ENSURE( pFormat, ":-( No Format given for Fly." );
+ pFly = new SwFlyLayFrame( pFormat, this, this );
+ AppendFly( pFly );
+ ::RegistFlys( this, pFly );
+ }
+ }
+}
+
+// #i18732# - adjustments for following text flow or not
+// AND alignment at 'page areas' for to paragraph/to character anchored objects
+// #i22305# - adjustment for following text flow for to frame anchored objects
+// #i29778# - Because calculating the floating screen object's position
+// (Writer fly frame or drawing object) doesn't perform a calculation on its
+// upper frames and its anchor frame, a calculation of the upper frames in this
+// method is no longer sensible.
+// #i28701# - if document compatibility option 'Consider wrapping style influence
+// on object positioning' is ON, the clip area corresponds to the one as the
+// object doesn't follow the text flow.
+bool CalcClipRect( const SdrObject *pSdrObj, SwRect &rRect, bool bMove )
+{
+ bool bRet = true;
+ if ( auto pVirtFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>(pSdrObj) )
+ {
+ const SwFlyFrame* pFly = pVirtFlyDrawObj->GetFlyFrame();
+ const bool bFollowTextFlow = pFly->GetFormat()->GetFollowTextFlow().GetValue();
+ // #i28701#
+ const bool bConsiderWrapOnObjPos =
+ pFly->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);
+ const SwFormatVertOrient &rV = pFly->GetFormat()->GetVertOrient();
+ if( pFly->IsFlyLayFrame() )
+ {
+ const SwFrame* pClip;
+ // #i22305#
+ // #i28701#
+ if ( !bFollowTextFlow || bConsiderWrapOnObjPos )
+ {
+ pClip = pFly->GetAnchorFrame()->FindPageFrame();
+ }
+ else
+ {
+ pClip = pFly->GetAnchorFrame();
+ }
+
+ rRect = pClip->getFrameArea();
+ SwRectFnSet aRectFnSet(pClip);
+
+ // vertical clipping: Top and Bottom, also to PrtArea if necessary
+ if( rV.GetVertOrient() != text::VertOrientation::NONE &&
+ rV.GetRelationOrient() == text::RelOrientation::PRINT_AREA )
+ {
+ aRectFnSet.SetTop( rRect, aRectFnSet.GetPrtTop(*pClip) );
+ aRectFnSet.SetBottom( rRect, aRectFnSet.GetPrtBottom(*pClip) );
+ }
+ // horizontal clipping: Top and Bottom, also to PrtArea if necessary
+ const SwFormatHoriOrient &rH = pFly->GetFormat()->GetHoriOrient();
+ if( rH.GetHoriOrient() != text::HoriOrientation::NONE &&
+ rH.GetRelationOrient() == text::RelOrientation::PRINT_AREA )
+ {
+ aRectFnSet.SetLeft( rRect, aRectFnSet.GetPrtLeft(*pClip) );
+ aRectFnSet.SetRight(rRect, aRectFnSet.GetPrtRight(*pClip));
+ }
+ }
+ else if( pFly->IsFlyAtContentFrame() )
+ {
+ // #i18732# - consider following text flow or not
+ // AND alignment at 'page areas'
+ const SwFrame* pVertPosOrientFrame = pFly->GetVertPosOrientFrame();
+ if ( !pVertPosOrientFrame )
+ {
+ OSL_FAIL( "::CalcClipRect(..) - frame, vertical position is oriented at, is missing .");
+ pVertPosOrientFrame = pFly->GetAnchorFrame();
+ }
+
+ if ( !bFollowTextFlow || bConsiderWrapOnObjPos )
+ {
+ const SwLayoutFrame* pClipFrame = pVertPosOrientFrame->FindPageFrame();
+ if (!pClipFrame)
+ {
+ OSL_FAIL("!pClipFrame: "
+ "if you can reproduce this please file a bug");
+ return false;
+ }
+ rRect = bMove ? pClipFrame->GetUpper()->getFrameArea()
+ : pClipFrame->getFrameArea();
+ // #i26945# - consider that a table, during
+ // its format, can exceed its upper printing area bottom.
+ // Thus, enlarge the clip rectangle, if such a case occurred
+ if ( pFly->GetAnchorFrame()->IsInTab() )
+ {
+ const SwTabFrame* pTabFrame = const_cast<SwFlyFrame*>(pFly)
+ ->GetAnchorFrameContainingAnchPos()->FindTabFrame();
+ SwRect aTmp( pTabFrame->getFramePrintArea() );
+ aTmp += pTabFrame->getFrameArea().Pos();
+ rRect.Union( aTmp );
+ // #i43913# - consider also the cell frame
+ const SwFrame* pCellFrame = const_cast<SwFlyFrame*>(pFly)
+ ->GetAnchorFrameContainingAnchPos()->GetUpper();
+ while ( pCellFrame && !pCellFrame->IsCellFrame() )
+ {
+ pCellFrame = pCellFrame->GetUpper();
+ }
+ if ( pCellFrame )
+ {
+ aTmp = pCellFrame->getFramePrintArea();
+ aTmp += pCellFrame->getFrameArea().Pos();
+ rRect.Union( aTmp );
+ }
+ }
+ }
+ else if ( rV.GetRelationOrient() == text::RelOrientation::PAGE_FRAME ||
+ rV.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ // new class <SwEnvironmentOfAnchoredObject>
+ objectpositioning::SwEnvironmentOfAnchoredObject
+ aEnvOfObj( bFollowTextFlow );
+ const SwLayoutFrame& rVertClipFrame =
+ aEnvOfObj.GetVertEnvironmentLayoutFrame( *pVertPosOrientFrame );
+ if ( rV.GetRelationOrient() == text::RelOrientation::PAGE_FRAME )
+ {
+ rRect = rVertClipFrame.getFrameArea();
+ }
+ else if ( rV.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ if ( rVertClipFrame.IsPageFrame() )
+ {
+ rRect = static_cast<const SwPageFrame&>(rVertClipFrame).PrtWithoutHeaderAndFooter();
+ }
+ else
+ {
+ rRect = rVertClipFrame.getFrameArea();
+ }
+ }
+ const SwLayoutFrame* pHoriClipFrame =
+ pFly->GetAnchorFrame()->FindPageFrame()->GetUpper();
+ SwRectFnSet aRectFnSet(pFly->GetAnchorFrame());
+ aRectFnSet.SetLeft( rRect, aRectFnSet.GetLeft(pHoriClipFrame->getFrameArea()) );
+ aRectFnSet.SetRight(rRect, aRectFnSet.GetRight(pHoriClipFrame->getFrameArea()));
+ }
+ else
+ {
+ // #i26945#
+ const SwFrame *pClip =
+ const_cast<SwFlyFrame*>(pFly)->GetAnchorFrameContainingAnchPos();
+ SwRectFnSet aRectFnSet(pClip);
+ const SwLayoutFrame *pUp = pClip->GetUpper();
+ const SwFrame *pCell = pUp->IsCellFrame() ? pUp : nullptr;
+ const SwFrameType nType = bMove
+ ? SwFrameType::Root | SwFrameType::Fly | SwFrameType::Header |
+ SwFrameType::Footer | SwFrameType::Ftn
+ : SwFrameType::Body | SwFrameType::Fly | SwFrameType::Header |
+ SwFrameType::Footer | SwFrameType::Cell| SwFrameType::Ftn;
+
+ while ( !(pUp->GetType() & nType) || pUp->IsColBodyFrame() )
+ {
+ pUp = pUp->GetUpper();
+ if ( !pCell && pUp->IsCellFrame() )
+ pCell = pUp;
+ }
+ if ( bMove && pUp->IsRootFrame() )
+ {
+ rRect = pUp->getFramePrintArea();
+ rRect += pUp->getFrameArea().Pos();
+ pUp = nullptr;
+ }
+ if ( pUp )
+ {
+ if ( pUp->GetType() & SwFrameType::Body )
+ {
+ const SwPageFrame *pPg;
+ if ( pUp->GetUpper() != (pPg = pFly->FindPageFrame()) )
+ pUp = pPg->FindBodyCont();
+ if (pUp)
+ {
+ rRect = pUp->GetUpper()->getFrameArea();
+ aRectFnSet.SetTop( rRect, aRectFnSet.GetPrtTop(*pUp) );
+ aRectFnSet.SetBottom(rRect, aRectFnSet.GetPrtBottom(*pUp));
+ }
+ }
+ else
+ {
+ if( ( pUp->GetType() & (SwFrameType::Fly | SwFrameType::Ftn ) ) &&
+ !pUp->getFrameArea().Contains( pFly->getFrameArea().Pos() ) )
+ {
+ if( pUp->IsFlyFrame() )
+ {
+ const SwFlyFrame *pTmpFly = static_cast<const SwFlyFrame*>(pUp);
+ while( pTmpFly->GetNextLink() )
+ {
+ pTmpFly = pTmpFly->GetNextLink();
+ if( pTmpFly->getFrameArea().Contains( pFly->getFrameArea().Pos() ) )
+ break;
+ }
+ pUp = pTmpFly;
+ }
+ else if( pUp->IsInFootnote() )
+ {
+ const SwFootnoteFrame *pTmp = pUp->FindFootnoteFrame();
+ while( pTmp->GetFollow() )
+ {
+ pTmp = pTmp->GetFollow();
+ if( pTmp->getFrameArea().Contains( pFly->getFrameArea().Pos() ) )
+ break;
+ }
+ pUp = pTmp;
+ }
+ }
+ rRect = pUp->getFramePrintArea();
+ rRect.Pos() += pUp->getFrameArea().Pos();
+ if ( pUp->GetType() & (SwFrameType::Header | SwFrameType::Footer) )
+ {
+ rRect.Left ( pUp->GetUpper()->getFrameArea().Left() );
+ rRect.Width( pUp->GetUpper()->getFrameArea().Width());
+ }
+ else if ( pUp->IsCellFrame() ) //MA_FLY_HEIGHT
+ {
+ const SwFrame *pTab = pUp->FindTabFrame();
+ aRectFnSet.SetBottom( rRect, aRectFnSet.GetPrtBottom(*pTab->GetUpper()) );
+ // expand to left and right cell border
+ rRect.Left ( pUp->getFrameArea().Left() );
+ rRect.Width( pUp->getFrameArea().Width() );
+ }
+ }
+ }
+ if ( pCell )
+ {
+ // CellFrames might also sit in unallowed areas. In this case,
+ // the Fly is allowed to do so as well
+ SwRect aTmp( pCell->getFramePrintArea() );
+ aTmp += pCell->getFrameArea().Pos();
+ rRect.Union( aTmp );
+ }
+ }
+ }
+ else
+ {
+ const SwFrame *pUp = pFly->GetAnchorFrame()->GetUpper();
+ SwRectFnSet aRectFnSet(pFly->GetAnchorFrame());
+ while( pUp->IsColumnFrame() || pUp->IsSctFrame() || pUp->IsColBodyFrame())
+ pUp = pUp->GetUpper();
+ rRect = pUp->getFrameArea();
+ if( !pUp->IsBodyFrame() )
+ {
+ rRect += pUp->getFramePrintArea().Pos();
+ rRect.SSize( pUp->getFramePrintArea().SSize() );
+ if ( pUp->IsCellFrame() )
+ {
+ const SwFrame *pTab = pUp->FindTabFrame();
+ aRectFnSet.SetBottom( rRect, aRectFnSet.GetPrtBottom(*pTab->GetUpper()) );
+ }
+ }
+ else if ( pUp->GetUpper()->IsPageFrame() )
+ {
+ // Objects anchored as character may exceed right margin
+ // of body frame:
+ aRectFnSet.SetRight( rRect, aRectFnSet.GetRight(pUp->GetUpper()->getFrameArea()) );
+ }
+ tools::Long nHeight = (9*aRectFnSet.GetHeight(rRect))/10;
+ tools::Long nTop;
+ const SwFormat *pFormat = GetUserCall(pSdrObj)->GetFormat();
+ const SvxULSpaceItem &rUL = pFormat->GetULSpace();
+ if( bMove )
+ {
+ nTop = aRectFnSet.IsVert() ? static_cast<const SwFlyInContentFrame*>(pFly)->GetRefPoint().X() :
+ static_cast<const SwFlyInContentFrame*>(pFly)->GetRefPoint().Y();
+ nTop = aRectFnSet.YInc( nTop, -nHeight );
+ tools::Long nWidth = aRectFnSet.GetWidth(pFly->getFrameArea());
+ aRectFnSet.SetLeftAndWidth( rRect, aRectFnSet.IsVert() ?
+ static_cast<const SwFlyInContentFrame*>(pFly)->GetRefPoint().Y() :
+ static_cast<const SwFlyInContentFrame*>(pFly)->GetRefPoint().X(), nWidth );
+ nHeight = 2*nHeight - rUL.GetLower() - rUL.GetUpper();
+ }
+ else
+ {
+ nTop = aRectFnSet.YInc( aRectFnSet.GetBottom(pFly->getFrameArea()),
+ rUL.GetLower() - nHeight );
+ nHeight = 2*nHeight - aRectFnSet.GetHeight(pFly->getFrameArea())
+ - rUL.GetLower() - rUL.GetUpper();
+ }
+ aRectFnSet.SetTopAndHeight( rRect, nTop, nHeight );
+ }
+ }
+ else
+ {
+ const SwDrawContact *pC = static_cast<const SwDrawContact*>(GetUserCall(pSdrObj));
+ const SwFrameFormat *pFormat = pC->GetFormat();
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ if ( RndStdIds::FLY_AS_CHAR == rAnch.GetAnchorId() )
+ {
+ const SwFrame* pAnchorFrame = pC->GetAnchorFrame( pSdrObj );
+ if( !pAnchorFrame )
+ {
+ OSL_FAIL( "<::CalcClipRect(..)> - missing anchor frame." );
+ const_cast<SwDrawContact*>(pC)->ConnectToLayout();
+ pAnchorFrame = pC->GetAnchorFrame();
+ }
+ const SwFrame* pUp = pAnchorFrame->GetUpper();
+ rRect = pUp->getFramePrintArea();
+ rRect += pUp->getFrameArea().Pos();
+ SwRectFnSet aRectFnSet(pAnchorFrame);
+ tools::Long nHeight = (9*aRectFnSet.GetHeight(rRect))/10;
+ tools::Long nTop;
+ const SvxULSpaceItem &rUL = pFormat->GetULSpace();
+ SwRect aSnapRect( pSdrObj->GetSnapRect() );
+ tools::Long nTmpH = 0;
+ if( bMove )
+ {
+ nTop = aRectFnSet.YInc( aRectFnSet.IsVert() ? pSdrObj->GetAnchorPos().X() :
+ pSdrObj->GetAnchorPos().Y(), -nHeight );
+ tools::Long nWidth = aRectFnSet.GetWidth(aSnapRect);
+ aRectFnSet.SetLeftAndWidth( rRect, aRectFnSet.IsVert() ?
+ pSdrObj->GetAnchorPos().Y() :
+ pSdrObj->GetAnchorPos().X(), nWidth );
+ }
+ else
+ {
+ // #i26791# - value of <nTmpH> is needed to
+ // calculate value of <nTop>.
+ nTmpH = aRectFnSet.IsVert() ? pSdrObj->GetCurrentBoundRect().GetWidth() :
+ pSdrObj->GetCurrentBoundRect().GetHeight();
+ nTop = aRectFnSet.YInc( aRectFnSet.GetTop(aSnapRect),
+ rUL.GetLower() + nTmpH - nHeight );
+ }
+ nHeight = 2*nHeight - nTmpH - rUL.GetLower() - rUL.GetUpper();
+ aRectFnSet.SetTopAndHeight( rRect, nTop, nHeight );
+ }
+ else
+ {
+ // restrict clip rectangle for drawing
+ // objects in header/footer to the page frame.
+ // #i26791#
+ const SwFrame* pAnchorFrame = pC->GetAnchorFrame( pSdrObj );
+ if ( pAnchorFrame && pAnchorFrame->FindFooterOrHeader() )
+ {
+ // clip frame is the page frame the header/footer is on.
+ const SwFrame* pClipFrame = pAnchorFrame->FindPageFrame();
+ rRect = pClipFrame->getFrameArea();
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flypos.cxx b/sw/source/core/layout/flypos.cxx
new file mode 100644
index 000000000..0a7ab2dca
--- /dev/null
+++ b/sw/source/core/layout/flypos.cxx
@@ -0,0 +1,65 @@
+/* -*- 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 <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <fmtanchr.hxx>
+#include <ndindex.hxx>
+#include <frameformats.hxx>
+#include <svx/swframetypes.hxx>
+
+bool SwPosFlyFrameCmp::operator()(const SwPosFlyFramePtr& rA, const SwPosFlyFramePtr& rB) const
+{
+ if (rA->GetNdIndex() == rB->GetNdIndex())
+ {
+ // In this case, the order number decides!
+ return rA->GetOrdNum() < rB->GetOrdNum();
+ }
+
+ return rA->GetNdIndex() < rB->GetNdIndex();
+}
+
+SwPosFlyFrame::SwPosFlyFrame(const SwNodeIndex& rIdx, const SwFrameFormat* pFormat,
+ sal_uInt16 nArrPos)
+ : m_pFrameFormat(pFormat)
+ , m_pNodeIndex(const_cast<SwNodeIndex*>(&rIdx))
+ , m_nOrdNum(SAL_MAX_UINT32)
+{
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId())
+ m_pNodeIndex = new SwNodeIndex(rIdx);
+ else if (pFormat->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell())
+ pFormat->CallSwClientNotify(sw::GetZOrderHint(m_nOrdNum));
+ if (m_nOrdNum == SAL_MAX_UINT32)
+ {
+ m_nOrdNum = pFormat->GetDoc()->GetSpzFrameFormats()->size();
+ m_nOrdNum += nArrPos;
+ }
+}
+
+SwPosFlyFrame::~SwPosFlyFrame()
+{
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId())
+ {
+ delete m_pNodeIndex;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx
new file mode 100644
index 000000000..5a23118be
--- /dev/null
+++ b/sw/source/core/layout/frmtool.cxx
@@ -0,0 +1,4040 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <svx/svdpage.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <sal/log.hxx>
+#include <o3tl/deleter.hxx>
+#include <osl/diagnose.h>
+
+#include <drawdoc.hxx>
+#include <fmtornt.hxx>
+#include <fmthdft.hxx>
+#include <fmtfsize.hxx>
+#include <fmtsrnd.hxx>
+#include <docary.hxx>
+#include <lineinfo.hxx>
+#include <swmodule.hxx>
+#include <pagefrm.hxx>
+#include <colfrm.hxx>
+#include <fesh.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <flyfrms.hxx>
+#include <layact.hxx>
+#include <pagedesc.hxx>
+#include <section.hxx>
+#include <sectfrm.hxx>
+#include <node2lay.hxx>
+#include <ndole.hxx>
+#include <hints.hxx>
+#include "layhelp.hxx"
+#include <laycache.hxx>
+#include <rootfrm.hxx>
+#include <paratr.hxx>
+#include <redline.hxx>
+#include <sortedobjs.hxx>
+#include <objectformatter.hxx>
+#include <calbck.hxx>
+#include <ndtxt.hxx>
+#include <undobj.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentTimerAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <frameformats.hxx>
+#include <boost/circular_buffer.hpp>
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+ // FIXME: would likely better be a member of SwRootFrame instead of a global flag
+ bool isFlyCreationSuppressed = false;
+}
+namespace sw {
+ FlyCreationSuppressor::FlyCreationSuppressor(bool wasAlreadySuppressedAllowed)
+ : m_wasAlreadySuppressed(isFlyCreationSuppressed)
+ {
+ (void)wasAlreadySuppressedAllowed;
+ assert(wasAlreadySuppressedAllowed || !isFlyCreationSuppressed);
+ isFlyCreationSuppressed = true;
+ }
+ FlyCreationSuppressor::~FlyCreationSuppressor()
+ {
+ isFlyCreationSuppressed = m_wasAlreadySuppressed;
+ }
+}
+
+bool bObjsDirect = true;
+bool bSetCompletePaintOnInvalidate = false;
+
+sal_uInt8 StackHack::s_nCnt = 0;
+bool StackHack::s_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<SwTextFrame*>(pF)->GetBaseOffsetForFly( true );
+ mnFlyAnchorOfstNoWrap = static_cast<SwTextFrame*>(pF)->GetBaseOffsetForFly( false );
+ }
+ else
+ {
+ mnFlyAnchorOfst = 0;
+ mnFlyAnchorOfstNoWrap = 0;
+ }
+
+ mbHadFollow = pF->IsContentFrame() && static_cast<SwContentFrame*>(pF)->GetFollow();
+}
+
+SwFrameNotify::~SwFrameNotify()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+void SwFrameNotify::ImplDestroy()
+{
+ 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<SwTextFrame*>(mpFrame)->GetBaseOffsetForFly( true ) ) ||
+ ( mnFlyAnchorOfstNoWrap != static_cast<SwTextFrame*>(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<SwTabFrame*>(pPre);
+ if ( pPreTab->GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) )
+ {
+ SwRowFrame* pLastRow = static_cast<SwRowFrame*>(pPreTab->GetLastLower());
+ if ( pLastRow && pLastRow->ShouldRowKeepWithNext() )
+ bInvalidPrePos = true;
+ }
+ }
+
+ if ( bInvalidPrePos )
+ pPre->InvalidatePos();
+ }
+ }
+ }
+ else if ( !pFlow->HasFollow() )
+ {
+ tools::Long nOldHeight = aRectFnSet.GetHeight(maFrame);
+ tools::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<SwSectionFrame*>(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<SwContentFrame*>(mpFrame)->GetFollow() )
+ {
+ if ( !mpFrame->IsTabFrame() || !static_cast<SwTabFrame*>(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 !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( mpFrame->IsAccessibleFrame() )
+ {
+ SwRootFrame *pRootFrame = mpFrame->getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( mpFrame, maFrame );
+ }
+ }
+#endif
+
+ // 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() &&
+ pObj->DynCastFlyFrame() != nullptr )
+ {
+ // determine to-fly anchored Writer fly frame
+ SwFlyFrame* pFlyFrame = static_cast<SwFlyFrame*>(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 ( auto pFlyFrame = pObj->DynCastFlyFrame() )
+ {
+ if ( bNotifySize )
+ pFlyFrame->InvalidateSize_();
+ // #115759# - no invalidation of
+ // position for as-character anchored objects.
+ if ( !bAnchoredAsChar )
+ {
+ pFlyFrame->InvalidatePos_();
+ }
+ pFlyFrame->Invalidate_();
+ }
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( pObj) != nullptr )
+ {
+ // #115759# - no invalidation of
+ // position for as-character anchored objects.
+ if ( !bAnchoredAsChar )
+ {
+ pObj->InvalidateObjPos();
+ }
+ }
+ else
+ {
+ OSL_FAIL( "<SwContentNotify::~SwContentNotify()> - unknown anchored object type." );
+ }
+ }
+ }
+ }
+ }
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ else if( mpFrame->IsTextFrame() && mbValidSize != mpFrame->isFrameAreaSizeValid() )
+ {
+ SwRootFrame *pRootFrame = mpFrame->getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ pRootFrame->GetCurrShell()->Imp()->InvalidateAccessibleFrameContent( mpFrame );
+ }
+ }
+#endif
+
+ // #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() ))
+ return;
+
+ // #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() )
+ return;
+
+ 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<const SwFlyInContentFrame*>( 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<SwTextFrame*>(pLowerFrame)->Prepare( PrepareHint::FramePositionChanged );
+ }
+ else if ( pLowerFrame->IsTabFrame() )
+ {
+ pLowerFrame->InvalidatePrt();
+ }
+
+ pLowerFrame->InvalidateObjs( false );
+
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+}
+
+void SwLayNotify::ImplDestroy()
+{
+ SwLayoutFrame *pLay = static_cast<SwLayoutFrame*>(mpFrame);
+ SwRectFnSet aRectFnSet(pLay);
+ bool bNotify = false;
+ if ( pLay->getFramePrintArea().SSize() != maPrt.SSize() )
+ {
+ if ( !IsLowersComplete() )
+ {
+ bool bInvaPercent;
+
+ if ( pLay->IsRowFrame() )
+ {
+ bInvaPercent = true;
+ tools::Long nNew = aRectFnSet.GetHeight(pLay->getFramePrintArea());
+ if( nNew != aRectFnSet.GetHeight(maPrt) )
+ static_cast<SwRowFrame*>(pLay)->AdjustCells( nNew, true);
+ if( aRectFnSet.GetWidth(pLay->getFramePrintArea())
+ != aRectFnSet.GetWidth(maPrt) )
+ static_cast<SwRowFrame*>(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<SwTabFrame*>(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<SwFootnoteFrame*>(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 <pLay> 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<SwFootnoteFrame*>(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<SwFlyFrame*>(pLay)->GetAnchorFrame()
+ && static_cast<SwFlyFrame*>(pLay)->GetAnchorFrame()->IsFlyFrame() )
+ static_cast<SwFlyFrame*>(pLay)->AnchorFrame()->InvalidateSize();
+}
+
+SwLayNotify::~SwLayNotify()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+SwFlyNotify::SwFlyNotify( SwFlyFrame *pFlyFrame ) :
+ SwLayNotify( pFlyFrame ),
+ // #115759# - keep correct page frame - the page frame
+ // the Writer fly frame is currently registered at.
+ m_pOldPage( pFlyFrame->GetPageFrame() ),
+ m_aFrameAndSpace( pFlyFrame->GetObjRectWithSpaces() )
+{
+}
+
+void SwFlyNotify::ImplDestroy()
+{
+ SwFlyFrame *pFly = static_cast<SwFlyFrame*>(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, m_pOldPage, m_aFrameAndSpace, &maPrt );
+ // #i35640# - additional notify anchor text frame,
+ // if Writer fly frame has changed its page
+ if ( pFly->GetAnchorFrame()->IsTextFrame() &&
+ pFly->GetPageFrame() != m_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.
+ // <SwLayNotify::~SwLayNotify()> takes care of invalidation of lower
+ // floating screen objects by calling method <SwLayoutFrame::NotifyLowerObjs()>.
+
+ if ( pFly->IsFlyAtContentFrame() )
+ {
+ SwFrame *pNxt = pFly->AnchorFrame()->FindNext();
+ while (pNxt)
+ {
+ pNxt->InvalidatePos();
+ if (!pNxt->IsSctFrame())
+ {
+ break;
+ }
+ // invalidating pos of a section frame doesn't have much
+ // effect, so try again with its lower
+ pNxt = static_cast<SwSectionFrame*>(pNxt)->Lower();
+ }
+ }
+
+ // #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() )
+ return;
+ if (pFly->IsFlyFreeFrame())
+ {
+ if (static_cast<SwFlyFreeFrame*>(pFly)->IsNoMoveOnCheckClip())
+ return;
+ }
+
+ // #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() )
+ return;
+
+ // 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();
+}
+
+SwFlyNotify::~SwFlyNotify()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+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() )
+ return;
+
+ SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(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();
+ }
+ }
+}
+
+void SwContentNotify::ImplDestroy()
+{
+ SwContentFrame *pCnt = static_cast<SwContentFrame*>(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<SwSectionFrame*>(pPrevFrame)->GetSection() ) ||
+ ( pPrevFrame->IsTextFrame() &&
+ static_cast<SwTextFrame*>(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
+ // <pCnt> is also inside a section.
+ // Thus due to <mbBordersJoinedWithPrev>,
+ // <pCnt> 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<SwSectionFrame*>(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<SwNoTextFrame*>(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<const SwCursorShell*>( &rCurrentShell) != nullptr )
+ {
+ pFESh = static_cast<SwFEShell*>(&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
+ }
+ }
+ // ditto animated graphics
+ if ( getFrameArea().HasArea() && static_cast<SwNoTextFrame*>(pCnt)->HasAnimation() )
+ {
+ static_cast<SwNoTextFrame*>(pCnt)->StopAnimation();
+ pSh->InvalidateWindows( getFrameArea() );
+ }
+ }
+ }
+
+ if ( bFirst )
+ {
+ pCnt->SetRetouche(); //fix(13870)
+
+ SwDoc& rDoc = pCnt->IsTextFrame()
+ ? static_cast<SwTextFrame*>(pCnt)->GetDoc()
+ : static_cast<SwNoTextFrame*>(pCnt)->GetNode()->GetDoc();
+ if ( !rDoc.GetSpzFrameFormats()->empty() &&
+ rDoc.DoesContainAtPageObjWithContentAnchor() && !rDoc.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 = rDoc.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( "<SwContentNotify::~SwContentNotify()> - 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<SwTextFrame*>(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 <SwTextFrame::Format(..)>.
+ if ( !pCnt->IsTextFrame() )
+ return;
+
+ SwTextFrame* pMasterFrame = pCnt->IsFollow()
+ ? static_cast<SwTextFrame*>(pCnt)->FindMaster()
+ : static_cast<SwTextFrame*>(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() );
+ }
+ }
+ }
+}
+
+SwContentNotify::~SwContentNotify()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+// 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) )
+ return;
+
+ 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<SwDrawContact*>(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);
+ pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( pDrawVirtObj )) );
+
+ pDrawVirtObj->ActionChanged();
+ }
+ }
+ else
+ {
+ SwFlyFrame *pFly;
+ if( bFlyAtFly )
+ pFly = new SwFlyLayFrame( static_cast<SwFlyFrameFormat*>(pFormat), pFrame, pFrame );
+ else
+ pFly = new SwFlyAtContentFrame( static_cast<SwFlyFrameFormat*>(pFormat), pFrame, pFrame );
+ pFly->Lock();
+ pFrame->AppendFly( pFly );
+ pFly->Unlock();
+ if ( pPage )
+ ::RegistFlys( pPage, pFly );
+ }
+}
+
+static bool IsShown(SwNodeOffset const nIndex,
+ const SwFormatAnchor & rAnch,
+ std::vector<sw::Extent>::const_iterator const*const pIter,
+ std::vector<sw::Extent>::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<SwTextNode&>(*pFirstNode), 0),
+ SwPosition(const_cast<SwTextNode&>(*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);
+ if (*pIter == *pEnd && rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ { // tdf#149595 special case - it *could* be shown if first == last
+ return !IsDestroyFrameAnchoredAtChar(*rAnch.GetContentAnchor(),
+ SwPosition(const_cast<SwTextNode&>(*pFirstNode), 0),
+ SwPosition(const_cast<SwTextNode&>(*pLastNode), pLastNode->Len()));
+ }
+ 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<SwTextNode&>(
+ 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<SwTextNode&>(*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<sw::Extent>::const_iterator const*const pIter,
+ std::vector<sw::Extent>::const_iterator const*const pEnd,
+ SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
+{
+ std::vector<SwFrameFormat*> const & rFlys(rNode.GetAnchoredFlys());
+ for (SwFrameFormat * pFrameFormat : rFlys)
+ {
+ SwFormatAnchor const& rAnchor = pFrameFormat->GetAnchor();
+ if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
+ || rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ assert(rAnchor.GetContentAnchor()->nNode.GetIndex() == rNode.GetIndex());
+ if (!IsShown(rNode.GetIndex(), rAnchor, pIter, pEnd, pFirstNode, pLastNode))
+ {
+ pFrameFormat->DelFrames();
+ }
+ }
+ }
+}
+
+void AppendObjsOfNode(SwFrameFormats const*const pTable, SwNodeOffset const nIndex,
+ SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc *const pDoc,
+ std::vector<sw::Extent>::const_iterator const*const pIter,
+ std::vector<sw::Extent>::const_iterator const*const pEnd,
+ SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
+{
+#if OSL_DEBUG_LEVEL > 0
+ std::vector<SwFrameFormat*> 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<SwFrameFormat*> const & rFlys(rNode.GetAnchoredFlys());
+ for (size_t it = 0; it != rFlys.size(); )
+ {
+ SwFrameFormat *const pFormat = rFlys[it];
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ if ( rAnch.GetContentAnchor() &&
+ IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode))
+ {
+#if OSL_DEBUG_LEVEL > 0
+ std::vector<SwFrameFormat*>::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, SwNodeOffset const nIndex,
+ SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc *const pDoc)
+{
+ if (pFrame->IsTextFrame())
+ {
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pFrame));
+ if (sw::MergedPara const*const pMerged = pTextFrame->GetMergedPara())
+ {
+ std::vector<sw::Extent>::const_iterator iterFirst(pMerged->extents.begin());
+ std::vector<sw::Extent>::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);
+ SwNodeOffset const until = iter == pMerged->extents.end()
+ ? pMerged->pLastNode->GetIndex() + 1
+ : iter->pNode->GetIndex();
+ for (SwNodeOffset 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<SwFrameFormat*> 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<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ 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,
+ SwNodeOffset nIndex, bool bPages, SwNodeOffset 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<SwActualSection> pActualSection;
+ std::unique_ptr<SwLayHelper> 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, SwNodeOffset(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<SwSectionNode*>(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 == SwNodeOffset(0) || nIndex < nEndIndex; ++nIndex)
+ {
+ SwNode *pNd = pDoc->GetNodes()[nIndex];
+ if ( pNd->IsContentNode() )
+ {
+ SwContentNode* pNode = static_cast<SwContentNode*>(pNd);
+ if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
+ {
+ if (pNd->IsTextNode()
+ && pNd->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
+ { // must have a frame already
+ assert(static_cast<SwTextFrame*>(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 !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( pFrame->IsTextFrame() )
+ {
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ // no notification, if <SwViewShell> is in construction
+ if ( pViewShell && !pViewShell->IsInConstructor() &&
+ pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() &&
+ pFrame->FindPageFrame() != nullptr)
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ // #i68958#
+ // The information flags of the text frame are validated
+ // in methods <FindNextCnt(..)> and <FindPrevCnt(..)>.
+ // 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();
+ }
+ }
+#endif
+ // 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 && !isFlyCreationSuppressed )
+ AppendObjs( pTable, nIndex, pFrame, pPage, pDoc );
+ }
+ else if ( pNd->IsTableNode() )
+ { //Should we have encountered a table?
+ SwTableNode *pTableNode = static_cast<SwTableNode*>(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->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
+ {
+ assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
+ nIndex = pTableNode->EndOfSectionIndex();
+ continue; // skip it
+ }
+
+ pFrame = pTableNode->MakeFrame( pLay );
+
+ // skip tables deleted with track changes
+ if ( !static_cast<SwTabFrame*>(pFrame)->Lower() )
+ {
+ 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();
+
+ 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.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ // no notification, if <SwViewShell> is in construction
+ if ( pViewShell && !pViewShell->IsInConstructor() &&
+ pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() &&
+ pFrame->FindPageFrame() != nullptr)
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ if ( bObjsDirect && !pTable->empty() )
+ static_cast<SwTabFrame*>(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<SwTabFrame*>(pFrame);
+ while ( pTmpFrame )
+ {
+ pTmpFrame->CheckDirChange();
+ pTmpFrame = pTmpFrame->IsFollow() ? pTmpFrame->FindMaster() : nullptr;
+ }
+
+ }
+ else if ( pNd->IsSectionNode() )
+ {
+ if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
+ {
+ assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
+ continue; // skip it
+ }
+ SwSectionNode *pNode = static_cast<SwSectionNode*>(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<SwSectionFrame*>(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<SwSectionFrame*>(pFrame)->Init();
+ }
+ else
+ {
+ pFrame->InsertBehind( pLay, pPrv );
+ // OD 25.03.2003 #108339# - direct initialization of section
+ // after insertion in the layout
+ static_cast<SwSectionFrame*>(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<SwSectionFrame*>(pPrv)->ContainsContent();
+ if( pPrv && pPrv->IsTextFrame() )
+ static_cast<SwTextFrame*>(pPrv)->Prepare( PrepareHint::QuoVadis, nullptr, false );
+ }
+ }
+
+ if (nIndex + 1 == nEndIndex
+ // tdf#136452 may also be needed at end of section
+ || pNode->EndOfSectionIndex() - 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<SwSectionFrame const*>(pFrame->GetNext())->GetSection()->GetFormat()->GetSectionNode())
+ ? static_cast<SwSectionFrame *>(pFrame->GetNext())->ContainsContent()
+ : pFrame->GetNext());
+ if (pNext
+ && pNext->IsTextFrame()
+ && static_cast<SwTextFrame*>(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<SwSectionFrame*>(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.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ // no notification, if <SwViewShell> is in construction
+ if ( pViewShell && !pViewShell->IsInConstructor() &&
+ pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() &&
+ pFrame->FindPageFrame() != nullptr)
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ 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<SwLayoutFrame*>(pFrame);
+ if ( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
+ pLay = pLay->GetNextLayoutLeaf();
+ pPrv = nullptr;
+ }
+ }
+ else if ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )
+ {
+ if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
+ {
+ assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
+ continue; // skip it
+ }
+ if (pLayout->HasMergedParas() && !pNd->StartOfSectionNode()->IsCreateFrameWhenHidingRedlines())
+ { // tdf#135014 section break in fieldmark (start inside, end outside)
+ assert(pNd->StartOfSectionNode()->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<SwSectionFrame*>(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<SwSectionFrame*>(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<SwSectionFrame*>(pFrame) );
+
+ pLay = static_cast<SwLayoutFrame*>(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<SwStartNode*>(pNd)->GetStartNodeType() )
+ {
+ if (pLayout->HasMergedParas() && !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 && !isFlyCreationSuppressed )
+ {
+ SwFlyFrame* pFly = pLay->FindFlyFrame();
+ if( pFly )
+ AppendObjs( pTable, nIndex, pFly, pPage, pDoc );
+ }
+ }
+ else
+ {
+ assert(!pLayout->HasMergedParas()
+ || 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 ( !isFlyCreationSuppressed )
+ 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 );
+ SwNodeOffset nEndIdx = rEndIdx.GetIndex();
+ // TODO for multiple layouts there should be a loop here
+ SwNode* pNd = pDoc->GetNodes().FindPrvNxtFrameNode( aTmp,
+ pDoc->GetNodes()[ nEndIdx-1 ],
+ pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+ if ( pNd )
+ {
+ bool bApres = aTmp < rSttIdx;
+ SwNode2Layout aNode2Layout( *pNd, rSttIdx.GetIndex() );
+ sw::FrameMode eMode = sw::FrameMode::Existing;
+ ::std::vector<SwFrame*> frames;
+ while (SwFrame* pFrame = aNode2Layout.NextFrame())
+ { // tdf#150500 new frames may be created that end up merged on pNd
+ // so copy the currently existing ones; they shouldn't get deleted
+ frames.push_back(pFrame);
+ }
+ for (SwFrame *const pFrame : frames)
+ {
+ 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() > SwNodeOffset(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<SwColumnFrame*>(pFrame->FindColFrame());
+ if( pCol )
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ do
+ {
+ if( pCol && !pMove )
+ { // No successor so far, look into the next column
+ pMove = pCol->ContainsAny();
+ if( pCol->GetNext() )
+ pCol = static_cast<SwColumnFrame*>(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<SwColumnFrame*>(pCol->FindSctFrame()->FindColFrame());
+ if( pCol )
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ }
+ else
+ pCol = nullptr;
+ }
+ // skip invalid SectionFrames
+ while( pMove && pMove->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pMove)->GetSection() )
+ pMove = pMove->GetNext();
+ } while( !pMove && pCol );
+
+ if( pMove )
+ {
+ if ( pMove->IsContentFrame() )
+ pTmp = static_cast<SwContentFrame*>(pMove);
+ else if ( pMove->IsTabFrame() )
+ pTmp = static_cast<SwTabFrame*>(pMove);
+ else if ( pMove->IsSctFrame() )
+ {
+ pMove = static_cast<SwSectionFrame*>(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<SwSectionFrame*>(pMove)->GetSection() )
+ pMove = pMove->GetNext();
+ if( pMove && pMove->IsSctFrame() )
+ pMove = static_cast<SwSectionFrame*>(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 <bAllowMove>
+ if( !isFlyCreationSuppressed )
+ {
+ 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<SwSectionFrame*>(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 sw::BorderCacheOwner* pOwner, const SwFrame* pConstructor)
+ : SwCacheObj(pOwner)
+ , m_rAttrSet(pConstructor->IsContentFrame()
+ ? pConstructor->IsTextFrame()
+ ? static_cast<const SwTextFrame*>(pConstructor)->GetTextNodeForParaProps()->GetSwAttrSet()
+ : static_cast<const SwNoTextFrame*>(pConstructor)->GetNode()->GetSwAttrSet()
+ : static_cast<const SwLayoutFrame*>(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 = pConstructor->DynCastTextFrame();
+ if ( pTextFrame )
+ {
+ pTextFrame->GetTextNodeForParaProps()->ClearLRSpaceItemDueToListLevelIndents( m_rLR );
+ }
+ else if ( pConstructor->IsNoTextFrame() )
+ {
+ m_rLR = std::make_shared<SvxLRSpaceItem>(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 <m_bJoinedWithPrev>
+ // and <m_bJoinedWithNext>, which aren't initialized by default.
+ m_bCachedJoinedWithPrev = false;
+ m_bCachedJoinedWithNext = false;
+}
+
+SwBorderAttrs::~SwBorderAttrs()
+{
+ const_cast<sw::BorderCacheOwner*>(static_cast<sw::BorderCacheOwner const *>(m_pOwner))->m_bInCache = 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();
+
+ if (m_rLR)
+ {
+ bool bGutterAtTop = m_rAttrSet.GetDoc()->getIDocumentSettingAccess().get(
+ DocumentSettingId::GUTTER_AT_TOP);
+ if (bGutterAtTop)
+ {
+ // Decrease the print area: the top space is the sum of top and gutter margins.
+ m_nTop += m_rLR->GetGutterMargin();
+ }
+ }
+
+ m_bTop = false;
+}
+
+void SwBorderAttrs::CalcBottom_()
+{
+ m_nBottom = CalcBottomLine() + m_rUL.GetLower();
+ m_bBottom = false;
+}
+
+tools::Long SwBorderAttrs::CalcRight( const SwFrame* pCaller ) const
+{
+ tools::Long nRight=0;
+
+ if (!pCaller->IsTextFrame() || !static_cast<const SwTextFrame*>(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<const SwTextFrame*>(pCaller)->GetTextNodeForParaProps()->GetLeftMarginWithNum();
+ }
+
+ if (pCaller->IsPageFrame() && m_rLR)
+ {
+ const auto pPageFrame = static_cast<const SwPageFrame*>(pCaller);
+ bool bGutterAtTop = pPageFrame->GetFormat()->getIDocumentSettingAccess().get(
+ DocumentSettingId::GUTTER_AT_TOP);
+ if (!bGutterAtTop)
+ {
+ bool bRtlGutter = pPageFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_RTL_GUTTER)->GetValue();
+ tools::Long nGutterMargin = bRtlGutter ? m_rLR->GetGutterMargin() : m_rLR->GetRightGutterMargin();
+ // Decrease the print area: the right space is the sum of right and right gutter
+ // margins.
+ nRight += nGutterMargin;
+ }
+ }
+
+ 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 (auto pFly = pObject->DynCastFlyFrame())
+ {
+ if (pFly->Lower() && pFly->Lower()->IsTabFrame())
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+tools::Long SwBorderAttrs::CalcLeft( const SwFrame *pCaller ) const
+{
+ tools::Long nLeft=0;
+
+ if (!pCaller->IsTextFrame() || !static_cast<const SwTextFrame*>(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<const SwTextFrame*>(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<const SwTextFrame*>(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<const SwTextFrame*>(pCaller)->GetTextNodeForParaProps()->GetLeftMarginWithNum();
+ }
+
+ if (pCaller->IsPageFrame() && m_rLR)
+ {
+ const auto pPageFrame = static_cast<const SwPageFrame*>(pCaller);
+ bool bGutterAtTop = pPageFrame->GetFormat()->getIDocumentSettingAccess().get(
+ DocumentSettingId::GUTTER_AT_TOP);
+ if (!bGutterAtTop)
+ {
+ bool bRtlGutter = pPageFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_RTL_GUTTER)->GetValue();
+ tools::Long nGutterMargin = bRtlGutter ? m_rLR->GetRightGutterMargin() : m_rLR->GetGutterMargin();
+ // Decrease the print area: the left space is the sum of left and gutter margins.
+ nLeft += nGutterMargin;
+ }
+ }
+
+ 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 <CalcRight()> and <rCmpAttrs.CalcRight()>
+// 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 <CalcRight> with <rCmpAttrs.CalcRight>.
+ 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 <m_bJoinedWithPrev>
+// 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<const SwTextFrame*>(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 <m_bJoinedWithNext>
+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<const SwTextFrame*>(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 <m_bJoinedWithPrev>
+// 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<SwBorderAttrs*>(this)->CalcJoinedWithPrev( _rFrame, _pPrevFrame );
+ }
+
+ return m_bJoinedWithPrev;
+}
+
+bool SwBorderAttrs::JoinedWithNext( const SwFrame& _rFrame ) const
+{
+ if ( !m_bCachedJoinedWithNext )
+ {
+ const_cast<SwBorderAttrs*>(this)->CalcJoinedWithNext( _rFrame );
+ }
+
+ return m_bJoinedWithNext;
+}
+
+// OD 2004-02-26 #i25029# - added 2nd parameter <_pPrevFrame>, which is passed to
+// method <JoinedWithPrev>
+void SwBorderAttrs::GetTopLine_( const SwFrame& _rFrame,
+ const SwFrame* _pPrevFrame )
+{
+ sal_uInt16 nRet = CalcTopLine();
+
+ // OD 21.05.2003 #108789# - use new method <JoinWithPrev()>
+ // 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 <JoinWithPrev()>
+ 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 sw::BorderCacheOwner const* GetBorderCacheOwner(SwFrame const& rFrame)
+{
+ return rFrame.IsContentFrame()
+ ? static_cast<sw::BorderCacheOwner const*>(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<const SwTextFrame&>(rFrame).GetTextNodeForParaProps()
+ : static_cast<const SwNoTextFrame&>(rFrame).GetNode())
+ : static_cast<sw::BorderCacheOwner const*>(static_cast<const SwLayoutFrame&>(rFrame).GetFormat());
+}
+
+SwBorderAttrAccess::SwBorderAttrAccess( SwCache &rCach, const SwFrame *pFrame ) :
+ SwCacheAccess( rCach,
+ static_cast<void const *>(GetBorderCacheOwner(*pFrame)),
+ GetBorderCacheOwner(*pFrame)->IsInCache()),
+ m_pConstructor( pFrame )
+{
+}
+
+SwCacheObj *SwBorderAttrAccess::NewObj()
+{
+ const_cast<sw::BorderCacheOwner *>(static_cast<sw::BorderCacheOwner const *>(m_pOwner))->m_bInCache = true;
+ return new SwBorderAttrs( static_cast<sw::BorderCacheOwner const *>(m_pOwner), m_pConstructor );
+}
+
+SwBorderAttrs *SwBorderAttrAccess::Get()
+{
+ return static_cast<SwBorderAttrs*>(SwCacheAccess::Get());
+}
+
+SwOrderIter::SwOrderIter( const SwPageFrame *pPg ) :
+ m_pPage( pPg ),
+ m_pCurrent( nullptr )
+{
+}
+
+void SwOrderIter::Top()
+{
+ m_pCurrent = nullptr;
+ if ( !m_pPage->GetSortedObjs() )
+ return;
+
+ const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
+ if ( !pObjs->size() )
+ return;
+
+ sal_uInt32 nTopOrd = 0;
+ (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating
+ for (SwAnchoredObject* i : *pObjs)
+ {
+ const SdrObject* pObj = i->GetDrawObj();
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( 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<const SwVirtFlyDrawObj*>( 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<const SwVirtFlyDrawObj*>( 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() )
+ return;
+
+ const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
+ if ( !pObjs->size() )
+ return;
+
+ sal_uInt32 nOrd = 0;
+ (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating
+ for (SwAnchoredObject* i : *pObjs)
+ {
+ const SdrObject* pObj = i->GetDrawObj();
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( 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 ( auto pFlyFrame = pObj->DynCastFlyFrame() )
+ {
+ // #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 <GetPageFrame()>
+ if (SwPageFrame *pPg = pFlyFrame->GetPageFrame())
+ pPg->RemoveFlyFromPage(pFlyFrame);
+ }
+ }
+ // #115759# - remove also drawing objects from page
+ else if ( auto pDrawObj = dynamic_cast<SwAnchoredDrawObject*>( pObj) )
+ {
+ if (pObj->GetFrameFormat().GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ {
+ if (SwPageFrame *pPg = pObj->GetPageFrame())
+ pPg->RemoveDrawObjFromPage( *pDrawObj );
+ }
+ }
+ }
+}
+
+SwFrame *SaveContent( SwLayoutFrame *pLay, SwFrame *pStart )
+{
+ if( pLay->IsSctFrame() && pLay->Lower() && pLay->Lower()->IsColumnFrame() )
+ sw_RemoveFootnotes( static_cast<SwColumnFrame*>(pLay->Lower()), true, true );
+
+ SwFrame *pSav = pLay->ContainsAny();
+ if ( nullptr == pSav )
+ 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<SwContentFrame*>(pFloat) );
+ }
+ else if ( pFloat->IsTabFrame() || pFloat->IsSctFrame() )
+ {
+ SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(pFloat)->ContainsContent();
+ if( pCnt )
+ {
+ do
+ { if ( pCnt->GetDrawObjs() )
+ ::lcl_RemoveObjsFromPage( pCnt );
+ pCnt = pCnt->GetNextContentFrame();
+ } while ( pCnt && static_cast<SwLayoutFrame*>(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 ( auto pFlyFrame = pObj->DynCastFlyFrame() )
+ {
+ if (pFlyFrame->IsFlyFreeFrame())
+ {
+ _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<const SwAnchoredDrawObject*>( pObj) != nullptr )
+ {
+ if (pObj->GetFrameFormat().GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ {
+ pObj->InvalidateObjPos();
+ _pPage->AppendDrawObjToPage(
+ *static_cast<SwAnchoredDrawObject*>(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<SwFlowFrame*>(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<SwContentFrame*>(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<SwTextFrame*>(pSav)->GetCacheIdx() != USHRT_MAX )
+ static_cast<SwTextFrame*>(pSav)->Init(); // I am its friend
+
+ if ( pPage && pSav->GetDrawObjs() )
+ ::lcl_AddObjsToPage( static_cast<SwContentFrame*>(pSav), pPage );
+ }
+ else
+ { SwContentFrame *pBlub = static_cast<SwLayoutFrame*>(pSav)->ContainsContent();
+ if( pBlub )
+ {
+ do
+ { if ( pPage && pBlub->GetDrawObjs() )
+ ::lcl_AddObjsToPage( pBlub, pPage );
+ if( pBlub->IsTextFrame() && static_cast<SwTextFrame*>(pBlub)->HasFootnote() &&
+ static_cast<SwTextFrame*>(pBlub)->GetCacheIdx() != USHRT_MAX )
+ static_cast<SwTextFrame*>(pBlub)->Init(); // I am its friend
+ pBlub = pBlub->GetNextContentFrame();
+ } while ( pBlub && static_cast<SwLayoutFrame*>(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,
+ bool const bVeryFirstPage )
+{
+ assert(pUpper);
+ assert(pUpper->IsRootFrame());
+ assert(!pSibling || static_cast<SwLayoutFrame const*>(pUpper)->Lower() != pSibling); // currently no insert before 1st page
+ SwPageFrame *pRet;
+ SwDoc *pDoc = static_cast<SwLayoutFrame*>(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(bVeryFirstPage) : rDesc.GetRightFormat(bVeryFirstPage);
+ OSL_ENSURE( pFormat, "Descriptor without any format?!" );
+ bInsertEmpty = !bInsertEmpty;
+ }
+ if( bInsertEmpty )
+ {
+ SwPageDesc *pTmpDesc = pSibling && pSibling->GetPrev() ?
+ static_cast<SwPageFrame*>(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<SwSortedObjs*>(pAnch->GetDrawObjs());
+ for (SwAnchoredObject* pObj : *pObjs)
+ {
+ if (SwFlyFrame* pFly = pObj->DynCastFlyFrame())
+ {
+ // register (not if already known)
+ // #i28701# - use new method <GetPageFrame()>
+ 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<const SwLayoutFrame*>(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 <pOld>
+ 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.Overlaps( pFrame->getFrameArea() ) )
+ lcl_CheckFlowBack( static_cast<SwLayoutFrame*>(pFrame)->Lower(), rRect );
+ }
+ else if( !pFrame->GetNext() && nBottom > pFrame->getFrameArea().Bottom() )
+ {
+ if( pFrame->IsContentFrame() && static_cast<SwContentFrame*>(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() )
+ return;
+
+ SwRect aCntPrt( pCnt->getFramePrintArea() );
+ aCntPrt.Pos() += pCnt->getFrameArea().Pos();
+ if ( eHint == PrepareHint::FlyFrameAttributesChanged )
+ {
+ // #i35640# - use given rectangle <rRect> instead
+ // of current bound rectangle
+ if ( aCntPrt.Overlaps( rRect ) )
+ pCnt->Prepare( PrepareHint::FlyFrameAttributesChanged );
+ }
+ // #i23129# - only invalidate, if the text frame
+ // printing area overlaps with the given rectangle.
+ else if ( aCntPrt.Overlaps( rRect ) )
+ pCnt->Prepare( eHint, static_cast<void*>(&aCntPrt.Intersection_( rRect )) );
+ if ( !pCnt->GetDrawObjs() )
+ return;
+
+ const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ 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<const SwVirtFlyDrawObj*>( pObj) )
+ {
+ pFlyFrame = const_cast<SwVirtFlyDrawObj*>(pVirtFlyDrawObj)->GetFlyFrame();
+ pAnchor = pFlyFrame->AnchorFrame();
+ }
+ else
+ {
+ pFlyFrame = nullptr;
+ pAnchor = const_cast<SwFrame*>(
+ 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 <GetLastBoundRect()>
+ // instead of <GetCurrentBoundRect()>, because a recalculation
+ // of the bounding rectangle isn't intended here.
+ if (!isValidTableBeforeAnchor
+ && (pTab->getFrameArea().Overlaps(SwRect(pObj->GetLastBoundRect())) ||
+ pTab->getFrameArea().Overlaps(rRect)))
+ {
+ if ( !pFlyFrame || !pFlyFrame->IsLowerOf( pTab ) )
+ pTab->InvalidatePrt();
+ }
+ }
+ SwLayoutFrame* pCell = pCnt->GetUpper();
+ // #i40606# - use <GetLastBoundRect()>
+ // instead of <GetCurrentBoundRect()>, because a recalculation
+ // of the bounding rectangle isn't intended here.
+ if (!isValidTableBeforeAnchor && pCell->IsCellFrame() &&
+ ( pCell->getFrameArea().Overlaps( SwRect(pObj->GetLastBoundRect()) ) ||
+ pCell->getFrameArea().Overlaps( 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 ( pAnchoredObj->DynCastFlyFrame() != nullptr )
+ {
+ if( pAnchoredObj->GetDrawObj() == pObj )
+ continue;
+ SwFlyFrame *pFly = static_cast<SwFlyFrame*>(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().Contains( rPos ) )
+ {
+ if( pFrame->IsFootnoteFrame() )
+ {
+ const SwFootnoteFrame* pTmp = static_cast<const SwFootnoteFrame*>(pFrame)->GetFollow();
+ while( pTmp )
+ {
+ if( pTmp->getFrameArea().Contains( rPos ) )
+ return pTmp;
+ pTmp = pTmp->GetFollow();
+ }
+ }
+ else
+ {
+ SwFlyFrame* pTmp = const_cast<SwFlyFrame*>(pFrame->FindFlyFrame());
+ while( pTmp )
+ {
+ if( pTmp->getFrameArea().Contains( 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<const SwVirtFlyDrawObj*>(pObj))
+ {
+ const SwFlyFrame* pFly = pFlyDrawObj->GetFlyFrame();
+ pFrame = pFly->GetAnchorFrame();
+ aPos = pFly->getFrameArea().Pos();
+ }
+ else
+ {
+ pFrame = static_cast<SwDrawContact*>(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<const SwFlyFrame*>(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<const SwFlyFrame*>(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()) )
+ {
+ tools::Long nHeight = 0, nFlyAdd = 0;
+ do
+ {
+ tools::Long nLow = pLow->getFrameArea().Height();
+ if( pLow->IsTextFrame() && static_cast<SwTextFrame*>(pLow)->IsUndersized() )
+ nLow += static_cast<SwTextFrame*>(pLow)->GetParHeight()-pLow->getFramePrintArea().Height();
+ else if( pLow->IsSctFrame() && static_cast<SwSectionFrame*>(pLow)->IsUndersized() )
+ nLow += static_cast<SwSectionFrame*>(pLow)->Undersize();
+ nFlyAdd = std::max( tools::Long(0), 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
+ {
+ tools::Long nRstHeight = 0;
+ while (pLow && pLow->IsLayoutFrame())
+ {
+ nRstHeight += ::CalcRowRstHeight(static_cast<SwLayoutFrame*>(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<SwLayoutFrame*>(pLow)));
+ pLow = pLow->GetNext();
+ }
+ return nRstHeight;
+}
+
+const SwFrame* FindPage( const SwRect &rRect, const SwFrame *pPage )
+{
+ if ( !rRect.Overlaps( pPage->getFrameArea() ) )
+ {
+ const SwRootFrame* pRootFrame = static_cast<const SwRootFrame*>(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* m_pFrame;
+ bool m_bSet;
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+public:
+ SwFrameHolder()
+ : m_pFrame(nullptr)
+ , m_bSet(false)
+ {
+ }
+ void SetFrame( SwFrame* pHold );
+ SwFrame* GetFrame() { return m_pFrame; }
+ void Reset();
+ bool IsSet() const { return m_bSet; }
+};
+
+}
+
+void SwFrameHolder::SetFrame( SwFrame* pHold )
+{
+ m_bSet = true;
+ if (m_pFrame != pHold)
+ {
+ if (m_pFrame)
+ EndListening(*m_pFrame);
+ StartListening(*pHold);
+ m_pFrame = pHold;
+ }
+}
+
+void SwFrameHolder::Reset()
+{
+ if (m_pFrame)
+ EndListening(*m_pFrame);
+ m_bSet = false;
+ m_pFrame = nullptr;
+}
+
+void SwFrameHolder::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::Dying && &rBC == m_pFrame)
+ {
+ m_pFrame = nullptr;
+ }
+}
+
+SwFrame* GetFrameOfModify(SwRootFrame const*const pLayout, sw::BroadcastingModify const& rMod,
+ SwFrameType const nFrameType, SwPosition const*const pPos,
+ std::pair<Point, bool> const*const pViewPosAndCalcFrame)
+{
+ SwFrame *pMinFrame = nullptr, *pTmpFrame;
+ SwFrameHolder aHolder;
+ SwRect aCalcRect;
+ bool bClientIterChanged = false;
+
+ SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> 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<SwFlyFrame*>(pTmpFrame)->GetAnchorFrame() &&
+ FAR_AWAY == pTmpFrame->getFrameArea().Pos().getX() &&
+ FAR_AWAY == pTmpFrame->getFrameArea().Pos().getY() )
+ aCalcRect = static_cast<SwFlyFrame*>(pTmpFrame)->GetAnchorFrame()->getFrameArea();
+ else
+ aCalcRect = pTmpFrame->getFrameArea();
+
+ if (aCalcRect.Contains(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<SwTextFrame*>(pMinFrame)->GetFrameAtPos( *pPos );
+
+ return pMinFrame;
+}
+
+bool IsExtraData( const SwDoc *pDoc )
+{
+ const SwLineNumberInfo &rInf = pDoc->GetLineNumberInfo();
+ if (rInf.IsPaintLineNumbers() ||
+ rInf.IsCountInFlys() ||
+ (static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos()) != text::HoriOrientation::NONE &&
+ !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty()))
+ {
+ return true;
+ }
+
+ const SwEditShell* pSh = pDoc->GetEditShell();
+ const SwViewOption* pViewOptions = pSh ? pSh->GetViewOptions() : nullptr;
+ return pViewOptions && pViewOptions->IsShowOutlineContentVisibilityButton();
+}
+
+// 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 <obIsLineSpacingProportional>
+*/
+void GetSpacingValuesOfFrame( const SwFrame& rFrame,
+ SwTwips& onLowerSpacing,
+ SwTwips& onLineSpacing,
+ bool& obIsLineSpacingProportional,
+ bool bIdenticalStyles )
+{
+ if ( !rFrame.IsFlowFrame() )
+ {
+ onLowerSpacing = 0;
+ onLineSpacing = 0;
+ }
+ else
+ {
+ const SvxULSpaceItem& rULSpace = rFrame.GetAttrSet()->GetULSpace();
+ // check contextual spacing if the style of actual and next paragraphs are identical
+ if (bIdenticalStyles)
+ onLowerSpacing = (rULSpace.GetContext() ? 0 : rULSpace.GetLower());
+ else
+ onLowerSpacing = rULSpace.GetLower();
+
+ onLineSpacing = 0;
+ obIsLineSpacingProportional = false;
+ if ( rFrame.IsTextFrame() )
+ {
+ onLineSpacing = static_cast<const SwTextFrame&>(rFrame).GetLineSpace();
+ obIsLineSpacingProportional =
+ onLineSpacing != 0 &&
+ static_cast<const SwTextFrame&>(rFrame).GetLineSpace( true ) == 0;
+ }
+
+ OSL_ENSURE( onLowerSpacing >= 0 && onLineSpacing >= 0,
+ "<GetSpacingValuesOfFrame(..)> - 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<SwTextFrame const*>(pFrame)->GetTextNodeFirst()
+ : const_cast<SwFrame*>(pFrame)->GetDep()
+ : nullptr )
+{
+}
+
+/// Can be used to check if a frame has been deleted
+bool SwDeletionChecker::HasBeenDeleted() const
+{
+ if ( !mpFrame || !mpRegIn )
+ return false;
+
+ SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> 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: */
diff --git a/sw/source/core/layout/ftnfrm.cxx b/sw/source/core/layout/ftnfrm.cxx
new file mode 100644
index 000000000..652436eb4
--- /dev/null
+++ b/sw/source/core/layout/ftnfrm.cxx
@@ -0,0 +1,2987 @@
+/* -*- 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 <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <ftnidx.hxx>
+#include <pagefrm.hxx>
+#include <colfrm.hxx>
+#include <rootfrm.hxx>
+#include <frmtool.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <tabfrm.hxx>
+#include <pagedesc.hxx>
+#include <ftninfo.hxx>
+#include <sectfrm.hxx>
+#include <objectformatter.hxx>
+#include <viewopt.hxx>
+#include <calbck.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <IDocumentSettingAccess.hxx>
+
+#define ENDNOTE 0x80000000
+
+/// Search the position of an attribute in the FootnoteArray at the document,
+/// because all footnotes are located there, ordered by their index.
+static sal_uLong lcl_FindFootnotePos( const SwDoc *pDoc, const SwTextFootnote *pAttr )
+{
+ const SwFootnoteIdxs &rFootnoteIdxs = pDoc->GetFootnoteIdxs();
+
+ SwTextFootnote* pBla = const_cast<SwTextFootnote*>(pAttr);
+ SwFootnoteIdxs::const_iterator it = rFootnoteIdxs.find( pBla );
+ if ( it != rFootnoteIdxs.end() )
+ {
+ sal_uLong nRet = it - rFootnoteIdxs.begin();
+ if( pAttr->GetFootnote().IsEndNote() )
+ return nRet + ENDNOTE;
+ return nRet;
+ }
+ OSL_ENSURE( !pDoc, "FootnotePos not found." );
+ return 0;
+}
+
+bool SwFootnoteFrame::operator<( const SwTextFootnote* pTextFootnote ) const
+{
+ const SwDoc* pDoc = GetFormat()->GetDoc();
+ OSL_ENSURE( pDoc, "SwFootnoteFrame: Missing doc!" );
+ return lcl_FindFootnotePos( pDoc, GetAttr() ) <
+ lcl_FindFootnotePos( pDoc, pTextFootnote );
+}
+
+/*
+|*
+|* bool lcl_NextFootnoteBoss( SwFootnoteBossFrame* pBoss, SwPageFrame* pPage)
+|* sets pBoss on the next SwFootnoteBossFrame, which can either be a column
+|* or a page (without columns). If the page changes meanwhile,
+|* pPage contains the new page and this function returns true.
+|*
+|*/
+
+static bool lcl_NextFootnoteBoss( SwFootnoteBossFrame* &rpBoss, SwPageFrame* &rpPage,
+ bool bDontLeave )
+{
+ if( rpBoss->IsColumnFrame() )
+ {
+ if( rpBoss->GetNext() )
+ {
+ rpBoss = static_cast<SwFootnoteBossFrame*>(rpBoss->GetNext()); //next column
+ return false;
+ }
+ if( rpBoss->IsInSct() )
+ {
+ SwSectionFrame* pSct = rpBoss->FindSctFrame()->GetFollow();
+ if( pSct )
+ {
+ OSL_ENSURE( pSct->Lower() && pSct->Lower()->IsColumnFrame(),
+ "Where's the column?" );
+ rpBoss = static_cast<SwColumnFrame*>(pSct->Lower());
+ SwPageFrame* pOld = rpPage;
+ rpPage = pSct->FindPageFrame();
+ return pOld != rpPage;
+ }
+ else if( bDontLeave )
+ {
+ rpPage = nullptr;
+ rpBoss = nullptr;
+ return false;
+ }
+ }
+ }
+ rpPage = static_cast<SwPageFrame*>(rpPage->GetNext()); // next page
+ rpBoss = rpPage;
+ if( rpPage )
+ {
+ SwLayoutFrame* pBody = rpPage->FindBodyCont();
+ if( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ rpBoss = static_cast<SwFootnoteBossFrame*>(pBody->Lower()); // first column
+ }
+ return true;
+}
+
+/// @returns column number if pBoss is a column, otherwise 0.
+static sal_uInt16 lcl_ColumnNum( const SwFrame* pBoss )
+{
+ sal_uInt16 nRet = 0;
+ if( !pBoss->IsColumnFrame() )
+ return 0;
+ const SwFrame* pCol;
+ if( pBoss->IsInSct() )
+ {
+ pCol = pBoss->GetUpper()->FindColFrame();
+ if( pBoss->GetNext() || pBoss->GetPrev() )
+ {
+ while( pBoss )
+ {
+ ++nRet; // Section columns
+ pBoss = pBoss->GetPrev();
+ }
+ }
+ }
+ else
+ pCol = pBoss;
+ while( pCol )
+ {
+ nRet += 256; // Page columns
+ pCol = pCol->GetPrev();
+ }
+ return nRet;
+}
+
+SwFootnoteContFrame::SwFootnoteContFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
+ SwLayoutFrame( pFormat, pSib )
+{
+ mnFrameType = SwFrameType::FtnCont;
+}
+
+SwFootnoteFrame* SwFootnoteContFrame::AddChained(bool bAppend, SwFrame* pThis, bool bDefaultFormat)
+{
+ SwFootnoteFrame *pOld = pThis->FindFootnoteFrame();
+ SwFrameFormat *pFormat = pOld->GetFormat();
+ if (bDefaultFormat)
+ pFormat = pFormat->GetDoc()->GetDfltFrameFormat();
+
+ SwFootnoteFrame *pNew = new SwFootnoteFrame(pFormat, pOld, pOld->GetRef(), pOld->GetAttr());
+
+ if (bAppend)
+ {
+ if (pOld->GetFollow())
+ {
+ pNew->SetFollow(pOld->GetFollow());
+ pOld->GetFollow()->SetMaster(pNew);
+ }
+ pOld->SetFollow(pNew);
+ pNew->SetMaster(pOld);
+ }
+ else
+ {
+ if (pOld->GetMaster())
+ {
+ pNew->SetMaster(pOld->GetMaster());
+ pOld->GetMaster()->SetFollow(pNew);
+ }
+ pNew->SetFollow(pOld);
+ pOld->SetMaster(pNew);
+ }
+
+ return pNew;
+}
+
+// lcl_Undersize(..) walks over a SwFrame and its contents
+// and returns the sum of all requested TextFrame magnifications.
+
+static tools::Long lcl_Undersize( const SwFrame* pFrame )
+{
+ tools::Long nRet = 0;
+ SwRectFnSet aRectFnSet(pFrame);
+ if( pFrame->IsTextFrame() )
+ {
+ if( static_cast<const SwTextFrame*>(pFrame)->IsUndersized() )
+ {
+ // Does this TextFrame would like to be a little bit bigger?
+ nRet = static_cast<const SwTextFrame*>(pFrame)->GetParHeight() -
+ aRectFnSet.GetHeight(pFrame->getFramePrintArea());
+ if( nRet < 0 )
+ nRet = 0;
+ }
+ }
+ else if( pFrame->IsLayoutFrame() )
+ {
+ const SwFrame* pNxt = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
+ while( pNxt )
+ {
+ nRet += lcl_Undersize( pNxt );
+ pNxt = pNxt->GetNext();
+ }
+ }
+ return nRet;
+}
+
+namespace sw {
+
+SwTwips FootnoteSeparatorHeight(SwPageFootnoteInfo const& rInf)
+{
+ return rInf.GetTopDist() + rInf.GetBottomDist() + rInf.GetLineWidth();
+}
+
+} // namespace sw
+
+/// "format" the frame (Fixsize is not set here).
+void SwFootnoteContFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
+{
+ // calculate total border, only one distance to the top
+ const SwPageFrame* pPage = FindPageFrame();
+ const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();
+ const SwTwips nBorder = sw::FootnoteSeparatorHeight(rInf);
+ SwRectFnSet aRectFnSet(this);
+
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+
+ aRectFnSet.SetTop( aPrt, nBorder );
+ aRectFnSet.SetWidth( aPrt, aRectFnSet.GetWidth(getFrameArea()) );
+ aRectFnSet.SetHeight(aPrt, aRectFnSet.GetHeight(getFrameArea()) - nBorder );
+
+ if( aRectFnSet.GetHeight(aPrt) < 0 && !pPage->IsFootnotePage() )
+ {
+ setFrameAreaSizeValid(false);
+ }
+ }
+
+ if ( isFrameAreaSizeValid() )
+ return;
+
+ bool bGrow = pPage->IsFootnotePage();
+ if( bGrow )
+ {
+ const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ bGrow = false;
+ }
+ if( bGrow )
+ Grow( LONG_MAX );
+ else
+ {
+ // VarSize is determined based on the content plus the borders
+ SwTwips nRemaining = 0;
+ SwFrame *pFrame = m_pLower;
+ while ( pFrame )
+ { // lcl_Undersize(..) respects (recursively) TextFrames, which
+ // would like to be bigger. They are created especially in
+ // columnized borders, if these do not have their maximum
+ // size yet.
+ nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea()) + lcl_Undersize( pFrame );
+ pFrame = pFrame->GetNext();
+ }
+ // add the own border
+ nRemaining += nBorder;
+
+ SwTwips nDiff;
+ if( IsInSct() )
+ {
+ nDiff = -aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()) );
+ if( nDiff > 0 )
+ {
+ if( nDiff > aRectFnSet.GetHeight(getFrameArea()) )
+ {
+ nDiff = aRectFnSet.GetHeight(getFrameArea());
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, -nDiff );
+ aRectFnSet.AddHeight( aFrm, -nDiff );
+ }
+ }
+ nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
+ if ( nDiff > 0 )
+ Shrink( nDiff );
+ else if ( nDiff < 0 )
+ {
+ Grow( -nDiff );
+ // It may happen that there is less space available,
+ // than what the border needs - the size of the PrtArea
+ // will then be negative.
+ SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ if( nPrtHeight < 0 )
+ {
+ const SwTwips nTmpDiff = std::max( SwTwips(aRectFnSet.GetTop(getFramePrintArea())), -nPrtHeight );
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SubTop( aPrt, nTmpDiff );
+ }
+ }
+ }
+
+ setFrameAreaSizeValid(true);
+}
+
+SwTwips SwFootnoteContFrame::GrowFrame( SwTwips nDist, bool bTst, bool )
+{
+ // No check if FixSize since FootnoteContainer are variable up to their max. height.
+ // If the max. height is LONG_MAX, take as much space as needed.
+ // If the page is a special footnote page, take also as much as possible.
+ assert(GetUpper() && GetUpper()->IsFootnoteBossFrame());
+
+ SwRectFnSet aRectFnSet(this);
+ if( aRectFnSet.GetHeight(getFrameArea()) > 0 &&
+ nDist > ( LONG_MAX - aRectFnSet.GetHeight(getFrameArea()) ) )
+ nDist = LONG_MAX - aRectFnSet.GetHeight(getFrameArea());
+
+ SwFootnoteBossFrame *pBoss = static_cast<SwFootnoteBossFrame*>(GetUpper());
+ if( IsInSct() )
+ {
+ SwSectionFrame* pSect = FindSctFrame();
+ OSL_ENSURE( pSect, "GrowFrame: Missing SectFrame" );
+ // In a section, which has to maximize, a footnotecontainer is allowed
+ // to grow, when the section can't grow anymore.
+ if( !bTst && !pSect->IsColLocked() &&
+ pSect->ToMaximize( false ) && pSect->Growable() )
+ {
+ pSect->InvalidateSize();
+ return 0;
+ }
+ }
+ const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ SwPageFrame *pPage = pBoss->FindPageFrame();
+ if ( bBrowseMode || !pPage->IsFootnotePage() )
+ {
+ if ( pBoss->GetMaxFootnoteHeight() != LONG_MAX )
+ {
+ nDist = std::min( nDist,
+ SwTwips(pBoss->GetMaxFootnoteHeight() - aRectFnSet.GetHeight(getFrameArea())) );
+ if ( nDist <= 0 )
+ return 0;
+ }
+ // FootnoteBoss also influences the max value
+ if( !IsInSct() )
+ {
+ const SwTwips nMax = pBoss->GetVarSpace();
+ if ( nDist > nMax )
+ nDist = nMax;
+ if ( nDist <= 0 )
+ return 0;
+ }
+ }
+ else if( nDist > aRectFnSet.GetHeight(GetPrev()->getFrameArea()) )
+ // do not use more space than the body has
+ nDist = aRectFnSet.GetHeight(GetPrev()->getFrameArea());
+
+ tools::Long nAvail = 0;
+ if ( bBrowseMode )
+ {
+ nAvail = GetUpper()->getFramePrintArea().Height();
+ const SwFrame *pAvail = GetUpper()->Lower();
+ do
+ { nAvail -= pAvail->getFrameArea().Height();
+ pAvail = pAvail->GetNext();
+ } while ( pAvail );
+ if ( nAvail > nDist )
+ nAvail = nDist;
+ }
+
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) + nDist );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX( -nDist );
+ }
+ }
+ tools::Long nGrow = nDist - nAvail,
+ nReal = 0;
+ if ( nGrow > 0 )
+ {
+ SwNeighbourAdjust nAdjust = pBoss->NeighbourhoodAdjustment();
+ if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
+ nReal = AdjustNeighbourhood( nGrow, bTst );
+ else
+ {
+ if( SwNeighbourAdjust::GrowAdjust == nAdjust )
+ {
+ SwFrame* pFootnote = Lower();
+ if( pFootnote )
+ {
+ while( pFootnote->GetNext() )
+ pFootnote = pFootnote->GetNext();
+ if( static_cast<SwFootnoteFrame*>(pFootnote)->GetAttr()->GetFootnote().IsEndNote() )
+ {
+ nReal = AdjustNeighbourhood( nGrow, bTst );
+ nAdjust = SwNeighbourAdjust::GrowShrink; // no more AdjustNeighbourhood
+ }
+ }
+ }
+ nReal += pBoss->Grow( nGrow - nReal, bTst );
+ if( ( SwNeighbourAdjust::GrowAdjust == nAdjust || SwNeighbourAdjust::AdjustGrow == nAdjust )
+ && nReal < nGrow )
+ nReal += AdjustNeighbourhood( nGrow - nReal, bTst );
+ }
+ }
+
+ nReal += nAvail;
+
+ if ( !bTst )
+ {
+ if ( nReal != nDist )
+ {
+ nDist -= nReal;
+
+ // We can only respect the boundless wish so much
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.AddHeight( -nDist );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nDist );
+ }
+ }
+
+ // growing happens upwards, so successors to not need to be invalidated
+ if( nReal )
+ {
+ InvalidateSize_();
+ InvalidatePos_();
+ InvalidatePage( pPage );
+ }
+ }
+ return nReal;
+}
+
+SwTwips SwFootnoteContFrame::ShrinkFrame( SwTwips nDiff, bool bTst, bool bInfo )
+{
+ SwPageFrame *pPage = FindPageFrame();
+ bool bShrink = false;
+ if ( pPage )
+ {
+ if( !pPage->IsFootnotePage() )
+ bShrink = true;
+ else
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ bShrink = true;
+ }
+ }
+ if( bShrink )
+ {
+ SwTwips nRet = SwLayoutFrame::ShrinkFrame( nDiff, bTst, bInfo );
+ if( IsInSct() && !bTst )
+ FindSctFrame()->InvalidateNextPos();
+ if ( !bTst && nRet )
+ {
+ InvalidatePos_();
+ InvalidatePage( pPage );
+ }
+ return nRet;
+ }
+ return 0;
+}
+
+SwFootnoteFrame::SwFootnoteFrame( SwFrameFormat *pFormat, SwFrame* pSib, SwContentFrame *pCnt, SwTextFootnote *pAt ):
+ SwLayoutFrame( pFormat, pSib ),
+ mpFollow( nullptr ),
+ mpMaster( nullptr ),
+ mpReference( pCnt ),
+ mpAttribute( pAt ),
+ mbBackMoveLocked( false ),
+ // #i49383#
+ mbUnlockPosOfLowerObjs( true )
+{
+ mnFrameType = SwFrameType::Ftn;
+}
+
+void SwFootnoteFrame::InvalidateNxtFootnoteCnts( SwPageFrame const *pPage )
+{
+ if ( !GetNext() )
+ return;
+
+ SwFrame *pCnt = static_cast<SwLayoutFrame*>(GetNext())->ContainsAny();
+ if( !pCnt )
+ return;
+
+ pCnt->InvalidatePage( pPage );
+ pCnt->InvalidatePrt_();
+ do
+ { pCnt->InvalidatePos_();
+ if( pCnt->IsSctFrame() )
+ {
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
+ if( pTmp )
+ pTmp->InvalidatePos_();
+ }
+ pCnt->GetUpper()->InvalidateSize_();
+ pCnt = pCnt->FindNext();
+ } while ( pCnt && GetUpper()->IsAnLower( pCnt ) );
+}
+
+bool SwFootnoteFrame::IsDeleteForbidden() const
+{
+ if (SwLayoutFrame::IsDeleteForbidden())
+ return true;
+ // needs to be in sync with the ::Cut logic
+ const SwLayoutFrame *pUp = GetUpper();
+ if (pUp)
+ {
+ if (GetPrev())
+ return false;
+
+ // The last footnote takes its container along if it
+ // is deleted. Cut would put pUp->Lower() to the value
+ // of GetNext(), so if there is no GetNext then
+ // Cut would delete pUp. If that condition is true
+ // here then check if the container is delete-forbidden
+ return !GetNext() && pUp->IsDeleteForbidden();
+ }
+ return false;
+}
+
+void SwFootnoteFrame::Cut()
+{
+ if ( GetNext() )
+ GetNext()->InvalidatePos();
+ else if ( GetPrev() )
+ GetPrev()->SetRetouche();
+
+ // first move then shrink Upper
+ SwLayoutFrame *pUp = GetUpper();
+
+ // correct chaining
+ SwFootnoteFrame *pFootnote = this;
+ if ( pFootnote->GetFollow() )
+ pFootnote->GetFollow()->SetMaster( pFootnote->GetMaster() );
+ if ( pFootnote->GetMaster() )
+ pFootnote->GetMaster()->SetFollow( pFootnote->GetFollow() );
+ pFootnote->SetFollow( nullptr );
+ pFootnote->SetMaster( nullptr );
+
+ // cut all connections
+ RemoveFromLayout();
+
+ if ( !pUp )
+ return;
+
+ // The last footnote takes its container along
+ if (!pUp->Lower())
+ {
+ SwPageFrame *pPage = pUp->FindPageFrame();
+ if ( pPage )
+ {
+ SwLayoutFrame *pBody = pPage->FindBodyCont();
+ if( pBody && !pBody->ContainsContent() )
+ pPage->getRootFrame()->SetSuperfluous();
+ }
+ SwSectionFrame* pSect = pUp->FindSctFrame();
+ pUp->Cut();
+ SwFrame::DestroyFrame(pUp);
+ // If the last footnote container was removed from a column
+ // section without a Follow, then this section can be shrunk.
+ if( pSect && !pSect->ToMaximize( false ) && !pSect->IsColLocked() )
+ pSect->InvalidateSize_();
+ }
+ else
+ { if ( getFrameArea().Height() )
+ pUp->Shrink( getFrameArea().Height() );
+ pUp->SetCompletePaint();
+ pUp->InvalidatePage();
+ }
+}
+
+void SwFootnoteFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
+{
+ OSL_ENSURE( pParent, "no parent in Paste." );
+ OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
+ OSL_ENSURE( pParent != this, "I am my own parent." );
+ OSL_ENSURE( pSibling != this, "I am my own sibling." );
+ OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
+ "I am still somewhere registered." );
+
+ // insert into tree structure
+ InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
+
+ SwRectFnSet aRectFnSet(this);
+ if( aRectFnSet.GetWidth(getFrameArea())!=aRectFnSet.GetWidth(pParent->getFramePrintArea()) )
+ InvalidateSize_();
+ InvalidatePos_();
+ if (SwFrame *const pContent = ContainsContent())
+ { // tdf#139687 invalidate possibly stale top margin (computed from previous frame)
+ pContent->InvalidatePrt_();
+ }
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+ if (SwFootnoteFrame *const pNext = static_cast<SwFootnoteFrame *>(GetNext()))
+ {
+ pNext->InvalidatePos_();
+ if (SwFrame *const pContent = pNext->ContainsContent())
+ { // tdf#139687 invalidate possibly stale top margin (computed from previous frame)
+ pContent->InvalidatePrt_();
+ }
+ }
+ if( aRectFnSet.GetHeight(getFrameArea()) )
+ pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) );
+
+ // If the predecessor is the master and/or the successor is the Follow,
+ // then take their content and destroy them.
+ if ( GetPrev() && GetPrev() == GetMaster() )
+ {
+ OSL_ENSURE( SwFlowFrame::CastFlowFrame( GetPrev()->GetLower() ),
+ "Footnote without content?" );
+ SwFlowFrame::CastFlowFrame( GetPrev()->GetLower())->
+ MoveSubTree( this, GetLower() );
+ SwFrame *pDel = GetPrev();
+ assert(pDel != this);
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ }
+ if ( GetNext() && GetNext() == GetFollow() )
+ {
+ OSL_ENSURE( SwFlowFrame::CastFlowFrame( GetNext()->GetLower() ),
+ "Footnote without content?" );
+ SwFlowFrame::CastFlowFrame( GetNext()->GetLower() )->MoveSubTree( this );
+ SwFrame *pDel = GetNext();
+ assert(pDel != this);
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ }
+#if OSL_DEBUG_LEVEL > 0
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ if ( GetPrev() )
+ {
+ OSL_ENSURE( lcl_FindFootnotePos( pDoc, static_cast<SwFootnoteFrame*>(GetPrev())->GetAttr() ) <=
+ lcl_FindFootnotePos( pDoc, GetAttr() ), "Prev is not FootnotePrev" );
+ }
+ if ( GetNext() )
+ {
+ OSL_ENSURE( lcl_FindFootnotePos( pDoc, GetAttr() ) <=
+ lcl_FindFootnotePos( pDoc, static_cast<SwFootnoteFrame*>(GetNext())->GetAttr() ),
+ "Next is not FootnoteNext" );
+ }
+#endif
+ InvalidateNxtFootnoteCnts( pPage );
+}
+
+/// Return the next layout leaf in that the frame can be moved.
+/// New pages will only be created if specified by the parameter.
+SwLayoutFrame *SwFrame::GetNextFootnoteLeaf( MakePageType eMakePage )
+{
+ SwFootnoteBossFrame *pOldBoss = FindFootnoteBossFrame();
+ SwPageFrame* pOldPage = pOldBoss->FindPageFrame();
+ SwPageFrame* pPage;
+ SwFootnoteBossFrame *pBoss = pOldBoss->IsColumnFrame() ?
+ static_cast<SwFootnoteBossFrame*>(pOldBoss->GetNext()) : nullptr; // next column, if existing
+ if( pBoss )
+ pPage = nullptr;
+ else
+ {
+ if( pOldBoss->GetUpper()->IsSctFrame() )
+ { // this can only be in a column area
+ SwLayoutFrame* pNxt = pOldBoss->GetNextSctLeaf( eMakePage );
+ if( pNxt )
+ {
+ OSL_ENSURE( pNxt->IsColBodyFrame(), "GetNextFootnoteLeaf: Funny Leaf" );
+ pBoss = static_cast<SwFootnoteBossFrame*>(pNxt->GetUpper());
+ pPage = pBoss->FindPageFrame();
+ }
+ else
+ return nullptr;
+ }
+ else
+ {
+ // next page
+ pPage = static_cast<SwPageFrame*>(pOldPage->GetNext());
+ // skip empty pages
+ if( pPage && pPage->IsEmptyPage() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ pBoss = pPage;
+ }
+ }
+ // What do we have until here?
+ // pBoss != NULL, pPage==NULL => pBoss is the next column on the same page
+ // pBoss != NULL, pPage!=NULL => pBoss and pPage are the following page (empty pages skipped)
+ // pBoss == NULL => pPage == NULL, so there are no following pages
+
+ // If the footnote has already a Follow we do not need to search.
+ // However, if there are unwanted empty columns/pages between Footnote and Follow,
+ // create another Follow on the next best column/page and the rest will sort itself out.
+ SwFootnoteFrame *pFootnote = FindFootnoteFrame();
+ if ( pFootnote && pFootnote->GetFollow() )
+ {
+ SwFootnoteBossFrame* pTmpBoss = pFootnote->GetFollow()->FindFootnoteBossFrame();
+ // Following cases will be handled:
+ // 1. both "FootnoteBoss"es are neighboring columns/pages
+ // 2. the new one is the first column of a neighboring page
+ // 3. the new one is the first column in a section of the next page
+ while( pTmpBoss != pBoss && pTmpBoss && !pTmpBoss->GetPrev() )
+ pTmpBoss = pTmpBoss->GetUpper()->FindFootnoteBossFrame();
+ if( pTmpBoss == pBoss )
+ return pFootnote->GetFollow();
+ }
+
+ // If no pBoss could be found or it is a "wrong" page, we need a new page.
+ if ( !pBoss || ( pPage && pPage->IsEndNotePage() && !pOldPage->IsEndNotePage() ) )
+ {
+ if ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT )
+ {
+ pBoss = InsertPage( pOldPage, pOldPage->IsFootnotePage() );
+ static_cast<SwPageFrame*>(pBoss)->SetEndNotePage( pOldPage->IsEndNotePage() );
+ }
+ else
+ return nullptr;
+ }
+ if( pBoss->IsPageFrame() )
+ {
+ // If this page has columns, then go to the first one
+ SwLayoutFrame* pLay = pBoss->FindBodyCont();
+ if( pLay && pLay->Lower() && pLay->Lower()->IsColumnFrame() )
+ pBoss = static_cast<SwFootnoteBossFrame*>(pLay->Lower());
+ }
+ // found column/page - add myself
+ SwFootnoteContFrame *pCont = pBoss->FindFootnoteCont();
+ if ( !pCont && pBoss->GetMaxFootnoteHeight() &&
+ ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT ) )
+ pCont = pBoss->MakeFootnoteCont();
+ return pCont;
+}
+
+/// Get the preceding layout leaf in that the frame can be moved.
+SwLayoutFrame *SwFrame::GetPrevFootnoteLeaf( MakePageType eMakeFootnote )
+{
+ // The predecessor of a footnote is (if possible)
+ // the master of the chain of the footnote.
+ SwFootnoteFrame *pFootnote = FindFootnoteFrame();
+ SwLayoutFrame *pRet = pFootnote->GetMaster();
+
+ SwFootnoteBossFrame* pOldBoss = FindFootnoteBossFrame();
+ SwPageFrame *pOldPage = pOldBoss->FindPageFrame();
+
+ if ( !pOldBoss->GetPrev() && !pOldPage->GetPrev() )
+ return pRet; // there is neither a predecessor column nor page
+
+ if ( !pRet )
+ {
+ bool bEndn = pFootnote->GetAttr()->GetFootnote().IsEndNote();
+ SwFrame* pTmpRef = nullptr;
+ const IDocumentSettingAccess& rSettings
+ = pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
+ if( bEndn && pFootnote->IsInSct() )
+ {
+ SwSectionFrame* pSect = pFootnote->FindSctFrame();
+ if( pSect->IsEndnAtEnd() )
+ // Endnotes at the end of the section.
+ pTmpRef = pSect->FindLastContent( SwFindMode::LastCnt );
+ }
+ else if (bEndn && rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
+ {
+ // Endnotes at the end of the document.
+ SwPageFrame* pPage = getRootFrame()->GetLastPage();
+ assert(pPage);
+ SwFrame* pPrevPage = pPage->GetPrev();
+ if (pPrevPage)
+ {
+ // Have a last but one page, use that since we try to get a preceding frame.
+ assert(pPrevPage->IsPageFrame());
+ pPage = static_cast<SwPageFrame*>(pPrevPage);
+ }
+ pTmpRef = pPage->FindLastBodyContent();
+ }
+ if( !pTmpRef )
+ // Endnotes on a separate page.
+ pTmpRef = pFootnote->GetRef();
+ SwFootnoteBossFrame* pStop = pTmpRef->FindFootnoteBossFrame( !bEndn );
+
+ const sal_uInt16 nNum = pStop->GetPhyPageNum();
+
+ // Do not leave the corresponding page if the footnote should
+ // be shown at the document ending or the footnote is an endnote.
+ const bool bEndNote = pOldPage->IsEndNotePage();
+ const bool bFootnoteEndDoc = pOldPage->IsFootnotePage();
+ SwFootnoteBossFrame* pNxtBoss = pOldBoss;
+ SwSectionFrame *pSect = pNxtBoss->GetUpper()->IsSctFrame() ?
+ static_cast<SwSectionFrame*>(pNxtBoss->GetUpper()) : nullptr;
+
+ do
+ {
+ if( pNxtBoss->IsColumnFrame() && pNxtBoss->GetPrev() )
+ pNxtBoss = static_cast<SwFootnoteBossFrame*>(pNxtBoss->GetPrev()); // one column backwards
+ else // one page backwards
+ {
+ SwLayoutFrame* pBody = nullptr;
+ if( pSect )
+ {
+ if( pSect->IsFootnoteLock() )
+ {
+ if( pNxtBoss == pOldBoss )
+ return nullptr;
+ pStop = pNxtBoss;
+ }
+ else
+ {
+ pSect = pSect->FindMaster();
+ if( !pSect || !pSect->Lower() )
+ return nullptr;
+ OSL_ENSURE( pSect->Lower()->IsColumnFrame(),
+ "GetPrevFootnoteLeaf: Where's the column?" );
+ pNxtBoss = static_cast<SwFootnoteBossFrame*>(pSect->Lower());
+ pBody = pSect;
+ }
+ }
+ else
+ {
+ SwPageFrame* pPage = static_cast<SwPageFrame*>(pNxtBoss->FindPageFrame()->GetPrev());
+ if( !pPage || pPage->GetPhyPageNum() < nNum ||
+ bEndNote != pPage->IsEndNotePage() || bFootnoteEndDoc != pPage->IsFootnotePage() )
+ return nullptr; // no further pages found
+ pNxtBoss = pPage;
+ pBody = pPage->FindBodyCont();
+ }
+ // We have the previous page, we might need to find the last column of it
+ if( pBody )
+ {
+ if ( pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ {
+ pNxtBoss = static_cast<SwFootnoteBossFrame*>(pBody->GetLastLower());
+ }
+ }
+ }
+ SwFootnoteContFrame *pCont = pNxtBoss->FindFootnoteCont();
+ if ( pCont )
+ {
+ pRet = pCont;
+ break;
+ }
+ if ( pStop == pNxtBoss )
+ {
+ // Reached the column/page of the reference.
+ // Try to add a container and paste our content.
+ if ( eMakeFootnote == MAKEPAGE_FTN && pNxtBoss->GetMaxFootnoteHeight() )
+ pRet = pNxtBoss->MakeFootnoteCont();
+ break;
+ }
+ } while( !pRet );
+ }
+ if ( pRet )
+ {
+ const SwFootnoteBossFrame* pNewBoss = pRet->FindFootnoteBossFrame();
+ bool bJump = false;
+ if( pOldBoss->IsColumnFrame() && pOldBoss->GetPrev() ) // a previous column exists
+ bJump = pOldBoss->GetPrev() != static_cast<SwFrame const *>(pNewBoss); // did we chose it?
+ else if( pNewBoss->IsColumnFrame() && pNewBoss->GetNext() )
+ bJump = true; // there is another column after the boss (not the old boss)
+ else
+ {
+ // Will be reached only if old and new boss are both either pages or the last (new)
+ // or first (old) column of a page. In this case, check if pages were skipped.
+ const sal_uInt16 nDiff = pOldPage->GetPhyPageNum() - pRet->FindPageFrame()->GetPhyPageNum();
+ if ( nDiff > 2 ||
+ (nDiff > 1 && !static_cast<SwPageFrame*>(pOldPage->GetPrev())->IsEmptyPage()) )
+ bJump = true;
+ }
+ if( bJump )
+ SwFlowFrame::SetMoveBwdJump( true );
+ }
+ return pRet;
+}
+
+bool SwFrame::IsFootnoteAllowed() const
+{
+ if ( !IsInDocBody() )
+ return false;
+
+ if ( IsInTab() )
+ {
+ // no footnotes in repeated headlines
+ const SwTabFrame *pTab = const_cast<SwFrame*>(this)->ImplFindTabFrame();
+ assert(pTab);
+ if ( pTab->IsFollow() )
+ return !pTab->IsInHeadline( *this );
+ }
+ return true;
+}
+
+void SwRootFrame::UpdateFootnoteNums()
+{
+ // page numbering only if set at the document
+ if ( GetFormat()->GetDoc()->GetFootnoteInfo().m_eNum == FTNNUM_PAGE )
+ {
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
+ while ( pPage && !pPage->IsFootnotePage() )
+ {
+ pPage->UpdateFootnoteNum();
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ }
+}
+
+/// remove all footnotes (not the references) and all footnote pages
+void sw_RemoveFootnotes( SwFootnoteBossFrame* pBoss, bool bPageOnly, bool bEndNotes )
+{
+ do
+ {
+ SwFootnoteContFrame *pCont = pBoss->FindFootnoteCont();
+ if ( pCont )
+ {
+ SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ assert(pFootnote);
+ if ( bPageOnly )
+ while ( pFootnote->GetMaster() )
+ pFootnote = pFootnote->GetMaster();
+ do
+ {
+ SwFootnoteFrame *pNxt = static_cast<SwFootnoteFrame*>(pFootnote->GetNext());
+ if ( !pFootnote->GetAttr()->GetFootnote().IsEndNote() ||
+ bEndNotes )
+ {
+ pFootnote->GetRef()->Prepare( PrepareHint::FootnoteInvalidation, static_cast<void*>(pFootnote->GetAttr()) );
+ if ( bPageOnly && !pNxt )
+ pNxt = pFootnote->GetFollow();
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ }
+ pFootnote = pNxt;
+
+ } while ( pFootnote );
+ }
+ if( !pBoss->IsInSct() )
+ {
+ // A sectionframe with the Footnote/EndnAtEnd-flags may contain
+ // foot/endnotes. If the last lower frame of the bodyframe is
+ // a multicolumned sectionframe, it may contain footnotes, too.
+ SwLayoutFrame* pBody = pBoss->FindBodyCont();
+ if( pBody && pBody->Lower() )
+ {
+ SwFrame* pLow = pBody->Lower();
+ while (pLow)
+ {
+ if( pLow->IsSctFrame() && ( !pLow->GetNext() ||
+ static_cast<SwSectionFrame*>(pLow)->IsAnyNoteAtEnd() ) &&
+ static_cast<SwSectionFrame*>(pLow)->Lower() &&
+ static_cast<SwSectionFrame*>(pLow)->Lower()->IsColumnFrame() )
+ sw_RemoveFootnotes( static_cast<SwColumnFrame*>(static_cast<SwSectionFrame*>(pLow)->Lower()),
+ bPageOnly, bEndNotes );
+ pLow = pLow->GetNext();
+ }
+ }
+ }
+ // is there another column?
+ pBoss = pBoss->IsColumnFrame() ? static_cast<SwColumnFrame*>(pBoss->GetNext()) : nullptr;
+ } while( pBoss );
+}
+
+void SwRootFrame::RemoveFootnotes( SwPageFrame *pPage, bool bPageOnly, bool bEndNotes )
+{
+ if ( !pPage )
+ pPage = static_cast<SwPageFrame*>(Lower());
+
+ do
+ { // On columned pages we have to clean up in all columns
+ SwFootnoteBossFrame* pBoss;
+ SwLayoutFrame* pBody = pPage->FindBodyCont();
+ if( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ pBoss = static_cast<SwFootnoteBossFrame*>(pBody->Lower()); // the first column
+ else
+ pBoss = pPage; // no columns
+ sw_RemoveFootnotes( pBoss, bPageOnly, bEndNotes );
+ if ( !bPageOnly )
+ {
+ if ( pPage->IsFootnotePage() &&
+ (!pPage->IsEndNotePage() || bEndNotes) )
+ {
+ SwFrame *pDel = pPage;
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ }
+ else
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ else
+ break;
+
+ } while ( pPage );
+}
+
+/// Change the page template of the footnote pages
+void SwRootFrame::CheckFootnotePageDescs( bool bEndNote )
+{
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
+ while ( pPage && !pPage->IsFootnotePage() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ while ( pPage && pPage->IsEndNotePage() != bEndNote )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+
+ if ( pPage )
+ SwFrame::CheckPageDescs( pPage, false );
+}
+
+/** Insert a footnote container
+ *
+ * A footnote container is always placed directly behind the body text.
+ *
+ * The frame format (FrameFormat) is always the default frame format.
+ *
+ * @return footnote container frame
+ */
+SwFootnoteContFrame *SwFootnoteBossFrame::MakeFootnoteCont()
+{
+ SAL_WARN_IF(FindFootnoteCont(), "sw.core", "footnote container exists already");
+
+ SwFootnoteContFrame *pNew = new SwFootnoteContFrame( GetFormat()->GetDoc()->GetDfltFrameFormat(), this );
+ SwLayoutFrame *pLay = FindBodyCont();
+ pNew->Paste( this, pLay->GetNext() );
+ return pNew;
+}
+
+SwFootnoteContFrame *SwFootnoteBossFrame::FindFootnoteCont()
+{
+ SwFrame *pFrame = Lower();
+ while( pFrame && !pFrame->IsFootnoteContFrame() )
+ pFrame = pFrame->GetNext();
+
+#if OSL_DEBUG_LEVEL > 0
+ if ( pFrame )
+ {
+ SwFrame *pFootnote = pFrame->GetLower();
+ assert(pFootnote);
+ while ( pFootnote )
+ {
+ assert(pFootnote->IsFootnoteFrame() && "Neighbor of footnote must be a footnote");
+ pFootnote = pFootnote->GetNext();
+ }
+ }
+#endif
+
+ return static_cast<SwFootnoteContFrame*>(pFrame);
+}
+
+/// Search the next available footnote container.
+SwFootnoteContFrame *SwFootnoteBossFrame::FindNearestFootnoteCont( bool bDontLeave )
+{
+ SwFootnoteContFrame *pCont = nullptr;
+ if ( !GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
+ {
+ pCont = FindFootnoteCont();
+ if ( !pCont )
+ {
+ SwPageFrame *pPage = FindPageFrame();
+ SwFootnoteBossFrame* pBoss = this;
+ bool bEndNote = pPage->IsEndNotePage();
+ do
+ {
+ bool bChgPage = lcl_NextFootnoteBoss( pBoss, pPage, bDontLeave );
+ // Found another boss? When changing pages, also the endnote flag must match.
+ if( pBoss && ( !bChgPage || pPage->IsEndNotePage() == bEndNote ) )
+ pCont = pBoss->FindFootnoteCont();
+ } while ( !pCont && pPage );
+ }
+ }
+ return pCont;
+}
+
+SwFootnoteFrame *SwFootnoteBossFrame::FindFirstFootnote()
+{
+ // search for the nearest footnote container
+ SwFootnoteContFrame *pCont = FindNearestFootnoteCont();
+ if ( !pCont )
+ return nullptr;
+
+ // Starting from the first footnote, search the first
+ // footnote that is referenced by the current column/page
+
+ SwFootnoteFrame *pRet = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ const sal_uInt16 nRefNum = FindPageFrame()->GetPhyPageNum();
+ const sal_uInt16 nRefCol = lcl_ColumnNum( this );
+ sal_uInt16 nPgNum, nColNum; // page number, column number
+ SwFootnoteBossFrame* pBoss;
+ SwPageFrame* pPage;
+ if( pRet )
+ {
+ pBoss = pRet->GetRef()->FindFootnoteBossFrame();
+ OSL_ENSURE( pBoss, "FindFirstFootnote: No boss found" );
+ if( !pBoss )
+ return nullptr; // ?There must be a bug, but no GPF
+ pPage = pBoss->FindPageFrame();
+ nPgNum = pPage->GetPhyPageNum();
+ if ( nPgNum == nRefNum )
+ {
+ nColNum = lcl_ColumnNum( pBoss );
+ if( nColNum == nRefCol )
+ return pRet; // found
+ else if( nColNum > nRefCol )
+ return nullptr; // at least one column too far
+ }
+ else if ( nPgNum > nRefNum )
+ return nullptr; // at least one column too far
+ }
+ else
+ return nullptr;
+ // Done if Ref is on a subsequent page or on the same page in a subsequent column
+
+ do
+ {
+ while ( pRet->GetFollow() )
+ pRet = pRet->GetFollow();
+
+ SwFootnoteFrame *pNxt = static_cast<SwFootnoteFrame*>(pRet->GetNext());
+ if ( !pNxt )
+ {
+ pBoss = pRet->FindFootnoteBossFrame();
+ pPage = pBoss->FindPageFrame();
+ lcl_NextFootnoteBoss( pBoss, pPage, false ); // next FootnoteBoss
+ pCont = pBoss ? pBoss->FindNearestFootnoteCont() : nullptr;
+ if ( pCont )
+ pNxt = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ }
+ if ( pNxt )
+ {
+ pRet = pNxt;
+ pBoss = pRet->GetRef()->FindFootnoteBossFrame();
+ pPage = pBoss->FindPageFrame();
+ nPgNum = pPage->GetPhyPageNum();
+ if ( nPgNum == nRefNum )
+ {
+ nColNum = lcl_ColumnNum( pBoss );
+ if( nColNum == nRefCol )
+ break; // found
+ else if( nColNum > nRefCol )
+ pRet = nullptr; // at least one column too far
+ }
+ else if ( nPgNum > nRefNum )
+ pRet = nullptr; // at least a page too far
+ }
+ else
+ pRet = nullptr; // there is none
+ } while( pRet );
+ return pRet;
+}
+
+/// Get the first footnote of a given content
+const SwFootnoteFrame *SwFootnoteBossFrame::FindFirstFootnote( SwContentFrame const *pCnt ) const
+{
+ const SwFootnoteFrame *pRet = const_cast<SwFootnoteBossFrame*>(this)->FindFirstFootnote();
+ if ( pRet )
+ {
+ const sal_uInt16 nColNum = lcl_ColumnNum( this );
+ const sal_uInt16 nPageNum = GetPhyPageNum();
+ while ( pRet && (pRet->GetRef() != pCnt) )
+ {
+ while ( pRet->GetFollow() )
+ pRet = pRet->GetFollow();
+
+ if ( pRet->GetNext() )
+ pRet = static_cast<const SwFootnoteFrame*>(pRet->GetNext());
+ else
+ { SwFootnoteBossFrame *pBoss = const_cast<SwFootnoteBossFrame*>(pRet->FindFootnoteBossFrame());
+ SwPageFrame *pPage = pBoss->FindPageFrame();
+ lcl_NextFootnoteBoss( pBoss, pPage, false ); // next FootnoteBoss
+ SwFootnoteContFrame *pCont = pBoss ? pBoss->FindNearestFootnoteCont() : nullptr;
+ pRet = pCont ? static_cast<SwFootnoteFrame*>(pCont->Lower()) : nullptr;
+ }
+ if ( pRet )
+ {
+ const SwFootnoteBossFrame* pBoss = pRet->GetRef()->FindFootnoteBossFrame();
+ if( pBoss->GetPhyPageNum() != nPageNum ||
+ nColNum != lcl_ColumnNum( pBoss ) )
+ pRet = nullptr;
+ }
+ }
+ }
+ return pRet;
+}
+
+void SwFootnoteBossFrame::ResetFootnote( const SwFootnoteFrame *pCheck )
+{
+ // Destroy the incarnations of footnotes to an attribute, if they don't
+ // belong to pAssumed
+ OSL_ENSURE( !pCheck->GetMaster(), "given master is not a Master." );
+
+ SwNodeIndex aIdx( *pCheck->GetAttr()->GetStartNode(), 1 );
+ SwContentNode *pNd = aIdx.GetNode().GetContentNode();
+ if ( !pNd )
+ pNd = pCheck->GetFormat()->GetDoc()->
+ GetNodes().GoNextSection( &aIdx, true, false );
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
+ SwFrame* pFrame = aIter.First();
+ while( pFrame )
+ {
+ if( pFrame->getRootFrame() == pCheck->getRootFrame() )
+ {
+ SwFrame *pTmp = pFrame->GetUpper();
+ while ( pTmp && !pTmp->IsFootnoteFrame() )
+ pTmp = pTmp->GetUpper();
+
+ SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pTmp);
+ while ( pFootnote && pFootnote->GetMaster() )
+ pFootnote = pFootnote->GetMaster();
+ if ( pFootnote != pCheck )
+ {
+ while (pFootnote && !pFootnote->IsDeleteForbidden())
+ {
+ SwFootnoteFrame *pNxt = pFootnote->GetFollow();
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ pFootnote = pNxt;
+ }
+ }
+ }
+
+ pFrame = aIter.Next();
+ }
+}
+
+void SwFootnoteBossFrame::InsertFootnote( SwFootnoteFrame* pNew )
+{
+ // Place the footnote in front of the footnote whose attribute
+ // is in front of the new one (get position via the Doc).
+ // If there is no footnote in this footnote-boss yet, create a new container.
+ // If there is a container but no footnote for this footnote-boss yet, place
+ // the footnote behind the last footnote of the closest previous column/page.
+
+ ResetFootnote( pNew );
+ SwFootnoteFrame *pSibling = FindFirstFootnote();
+ bool bDontLeave = false;
+
+ // Ok, a sibling has been found, but is the sibling in an acceptable
+ // environment?
+ if( IsInSct() )
+ {
+ SwSectionFrame* pMySect = ImplFindSctFrame();
+ bool bEndnt = pNew->GetAttr()->GetFootnote().IsEndNote();
+ if( bEndnt )
+ {
+ const SwSectionFormat* pEndFormat = pMySect->GetEndSectFormat();
+ bDontLeave = nullptr != pEndFormat;
+ if( pSibling )
+ {
+ if( pEndFormat )
+ {
+ if( !pSibling->IsInSct() ||
+ !pSibling->ImplFindSctFrame()->IsDescendantFrom( pEndFormat ) )
+ pSibling = nullptr;
+ }
+ else if( pSibling->IsInSct() )
+ pSibling = nullptr;
+ }
+ }
+ else
+ {
+ bDontLeave = pMySect->IsFootnoteAtEnd();
+ if( pSibling )
+ {
+ if( pMySect->IsFootnoteAtEnd() )
+ {
+ if( !pSibling->IsInSct() ||
+ !pMySect->IsAnFollow( pSibling->ImplFindSctFrame() ) )
+ pSibling = nullptr;
+ }
+ else if( pSibling->IsInSct() )
+ pSibling = nullptr;
+ }
+ }
+ }
+
+ if( pSibling && pSibling->FindPageFrame()->IsEndNotePage() !=
+ FindPageFrame()->IsEndNotePage() )
+ pSibling = nullptr;
+
+ // use the Doc to find out the position
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ const sal_uLong nStPos = ::lcl_FindFootnotePos( pDoc, pNew->GetAttr() );
+
+ sal_uLong nCmpPos = 0;
+ sal_uLong nLastPos = 0;
+ SwFootnoteContFrame *pParent = nullptr;
+ if( pSibling )
+ {
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
+ if( nCmpPos > nStPos )
+ pSibling = nullptr;
+ }
+
+ if ( !pSibling )
+ { pParent = FindFootnoteCont();
+ if ( !pParent )
+ {
+ // There is no footnote container yet. Before creating one, keep in mind that
+ // there might exist another following footnote that must be placed before the
+ // new inserted one e.g. because it was divided over multiple pages etc.
+ pParent = FindNearestFootnoteCont( bDontLeave );
+ if ( pParent )
+ {
+ SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pParent->Lower());
+ if ( pFootnote )
+ {
+
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pFootnote->GetAttr() );
+ if ( nCmpPos > nStPos )
+ pParent = nullptr;
+ }
+ else
+ pParent = nullptr;
+ }
+ }
+ if ( !pParent )
+ // here, we are sure that we can create a footnote container
+ pParent = MakeFootnoteCont();
+ else
+ {
+ // Based on the first footnote below the Parent, search for the first footnote whose
+ // index is after the index of the newly inserted, to place the new one correctly
+ pSibling = static_cast<SwFootnoteFrame*>(pParent->Lower());
+ if ( !pSibling )
+ {
+ OSL_ENSURE( false, "Could not find space for footnote.");
+ return;
+ }
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
+
+ SwFootnoteBossFrame *pNxtB; // remember the last one to not
+ SwFootnoteFrame *pLastSib = nullptr; // go too far.
+
+ while ( pSibling && nCmpPos <= nStPos )
+ {
+ pLastSib = pSibling; // potential candidate
+ nLastPos = nCmpPos;
+
+ while ( pSibling->GetFollow() )
+ pSibling = pSibling->GetFollow();
+
+ if ( pSibling->GetNext() )
+ {
+ pSibling = static_cast<SwFootnoteFrame*>(pSibling->GetNext());
+ OSL_ENSURE( !pSibling->GetMaster() || ( ENDNOTE > nStPos &&
+ pSibling->GetAttr()->GetFootnote().IsEndNote() ),
+ "InsertFootnote: Master expected I" );
+ }
+ else
+ {
+ pNxtB = pSibling->FindFootnoteBossFrame();
+ SwPageFrame *pSibPage = pNxtB->FindPageFrame();
+ bool bEndNote = pSibPage->IsEndNotePage();
+ bool bChgPage = lcl_NextFootnoteBoss( pNxtB, pSibPage, bDontLeave );
+ // When changing pages, also the endnote flag must match.
+ SwFootnoteContFrame *pCont = pNxtB && ( !bChgPage ||
+ pSibPage->IsEndNotePage() == bEndNote )
+ ? pNxtB->FindNearestFootnoteCont( bDontLeave ) : nullptr;
+ if( pCont )
+ pSibling = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ else // no further FootnoteContainer, insert after pSibling
+ break;
+ }
+ if ( pSibling )
+ {
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
+ OSL_ENSURE( nCmpPos > nLastPos, "InsertFootnote: Order of FootnoteFrame's buggy" );
+ }
+ }
+ // pLastSib is the last footnote before the new one and
+ // pSibling is empty or the first one after the new one
+ if ( pSibling && pLastSib && (pSibling != pLastSib) )
+ {
+ // too far?
+ if ( nCmpPos > nStPos )
+ pSibling = pLastSib;
+ }
+ else if ( !pSibling )
+ {
+ // Last chance: Take the last footnote of the parent.
+ // Special case that happens e.g. when moving paragraphs with multiple footnotes.
+ // To keep the order, use the parent of the last inspected footnote.
+ pSibling = pLastSib;
+ while( pSibling->GetFollow() )
+ pSibling = pSibling->GetFollow();
+ OSL_ENSURE( !pSibling->GetNext(), "InsertFootnote: Who's that guy?" );
+ }
+ }
+ }
+ else
+ {
+ // First footnote of the column/page found. Now search from there for the first one on the
+ // same column/page whose index is after the given one. The last one found is the predecessor.
+ SwFootnoteBossFrame* pBoss = pNew->GetRef()->FindFootnoteBossFrame(
+ !pNew->GetAttr()->GetFootnote().IsEndNote() );
+ sal_uInt16 nRefNum = pBoss->GetPhyPageNum(); // page number of the new footnote
+ sal_uInt16 nRefCol = lcl_ColumnNum( pBoss ); // column number of the new footnote
+ bool bEnd = false;
+ SwFootnoteFrame *pLastSib = nullptr;
+ while ( pSibling && !bEnd && (nCmpPos <= nStPos) )
+ {
+ pLastSib = pSibling;
+ nLastPos = nCmpPos;
+
+ while ( pSibling->GetFollow() )
+ pSibling = pSibling->GetFollow();
+
+ SwFootnoteFrame *pFoll = static_cast<SwFootnoteFrame*>(pSibling->GetNext());
+ if ( pFoll )
+ {
+ pBoss = pSibling->GetRef()->FindFootnoteBossFrame( !pSibling->
+ GetAttr()->GetFootnote().IsEndNote() );
+ sal_uInt16 nTmpRef;
+ if( nStPos >= ENDNOTE ||
+ (nTmpRef = pBoss->GetPhyPageNum()) < nRefNum ||
+ ( nTmpRef == nRefNum && lcl_ColumnNum( pBoss ) <= nRefCol ))
+ pSibling = pFoll;
+ else
+ bEnd = true;
+ }
+ else
+ {
+ SwFootnoteBossFrame* pNxtB = pSibling->FindFootnoteBossFrame();
+ SwPageFrame *pSibPage = pNxtB->FindPageFrame();
+ bool bEndNote = pSibPage->IsEndNotePage();
+ bool bChgPage = lcl_NextFootnoteBoss( pNxtB, pSibPage, bDontLeave );
+ // When changing pages, also the endnote flag must match.
+ SwFootnoteContFrame *pCont = pNxtB && ( !bChgPage ||
+ pSibPage->IsEndNotePage() == bEndNote )
+ ? pNxtB->FindNearestFootnoteCont( bDontLeave ) : nullptr;
+ if ( pCont )
+ pSibling = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ else
+ bEnd = true;
+ }
+ if ( !bEnd && pSibling )
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
+ if (pSibling && (pSibling != pLastSib))
+ {
+ // too far?
+ if ( (nLastPos < nCmpPos) && (nCmpPos > nStPos) )
+ {
+ pSibling = pLastSib;
+ bEnd = true;
+ }
+ }
+ }
+ }
+ if ( pSibling )
+ {
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
+ if ( nCmpPos < nStPos )
+ {
+ while ( pSibling->GetFollow() )
+ pSibling = pSibling->GetFollow();
+ pParent = static_cast<SwFootnoteContFrame*>(pSibling->GetUpper());
+ pSibling = static_cast<SwFootnoteFrame*>(pSibling->GetNext());
+ }
+ else
+ {
+ if( pSibling->GetMaster() )
+ {
+ if( ENDNOTE > nCmpPos || nStPos >= ENDNOTE )
+ {
+ OSL_FAIL( "InsertFootnote: Master expected II" );
+ do
+ pSibling = pSibling->GetMaster();
+ while ( pSibling->GetMaster() );
+ }
+ }
+ pParent = static_cast<SwFootnoteContFrame*>(pSibling->GetUpper());
+ }
+ }
+ OSL_ENSURE( pParent, "paste in space?" );
+ pNew->Paste( pParent, pSibling );
+}
+
+static SwPageFrame* lcl_GetApproximateFootnotePage(const bool bEnd, const SwPageFrame* pPage,
+ const SwDoc *pDoc, const SwTextFootnote *pAttr)
+{
+ // We can at least search the approximately correct page
+ // to ensure that we will finish in finite time even if
+ // hundreds of footnotes exist.
+ const SwPageFrame *pNxt = static_cast<const SwPageFrame*>(pPage->GetNext());
+ const sal_uLong nStPos = ::lcl_FindFootnotePos(pDoc, pAttr);
+ while (pNxt && (bEnd ? pNxt->IsEndNotePage() : pNxt->IsFootnotePage() && !pNxt->IsEndNotePage()))
+ {
+ const SwFootnoteContFrame *pCont = pNxt->FindFootnoteCont();
+ if (pCont && pCont->Lower())
+ {
+ OSL_ENSURE( pCont->Lower()->IsFootnoteFrame(), "no footnote in the container" );
+ if (nStPos > ::lcl_FindFootnotePos(pDoc,
+ static_cast<const SwFootnoteFrame*>(pCont->Lower())->GetAttr()))
+ {
+ pPage = pNxt;
+ pNxt = static_cast<const SwPageFrame*>(pPage->GetNext());
+ continue;
+ }
+ }
+ break;
+ }
+ return const_cast<SwPageFrame*>(pPage);
+}
+
+void SwFootnoteBossFrame::AppendFootnote( SwContentFrame *pRef, SwTextFootnote *pAttr )
+{
+ // If the footnote already exists, do nothing.
+ if ( FindFootnote( pRef, pAttr ) )
+ return;
+
+ // If footnotes are inserted at the end of the document,
+ // we only need to search from the relevant page on.
+ // If there is none yet, we need to create one.
+ // If it is an Endnote, we need to search for or create an
+ // Endnote page.
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ SwFootnoteBossFrame *pBoss = this;
+ SwPageFrame *pPage = FindPageFrame();
+ SwPageFrame *pMyPage = pPage;
+ bool bChgPage = false;
+ const bool bEnd = pAttr->GetFootnote().IsEndNote();
+ if (bEnd)
+ {
+ const IDocumentSettingAccess& rSettings = *pAttr->GetTextNode().getIDocumentSettingAccess();
+ if( GetUpper()->IsSctFrame() &&
+ static_cast<SwSectionFrame*>(GetUpper())->IsEndnAtEnd() )
+ {
+ // Endnotes at the end of the section.
+ SwFrame* pLast =
+ static_cast<SwSectionFrame*>(GetUpper())->FindLastContent( SwFindMode::EndNote );
+ if( pLast )
+ {
+ pBoss = pLast->FindFootnoteBossFrame();
+ pPage = pBoss->FindPageFrame();
+ }
+ }
+ else if (rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
+ {
+ // Endnotes at the end of the document.
+ pBoss = getRootFrame()->GetLastPage();
+ pPage = pBoss->FindPageFrame();
+ }
+ else
+ {
+ // Endnotes on a separate page.
+ while ( pPage->GetNext() && !pPage->IsEndNotePage() )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ bChgPage = true;
+ }
+ if ( !pPage->IsEndNotePage() )
+ {
+ SwPageDesc *pDesc = pDoc->GetEndNoteInfo().GetPageDesc( *pDoc );
+ pPage = ::InsertNewPage( *pDesc, pPage->GetUpper(),
+ !pPage->OnRightPage(), false, false, true, nullptr );
+ pPage->SetEndNotePage( true );
+ bChgPage = true;
+ }
+ else
+ pPage = lcl_GetApproximateFootnotePage(true, pPage, pDoc, pAttr);
+ }
+ }
+ else if( FTNPOS_CHAPTER == pDoc->GetFootnoteInfo().m_ePos && ( !GetUpper()->
+ IsSctFrame() || !static_cast<SwSectionFrame*>(GetUpper())->IsFootnoteAtEnd() ) )
+ {
+ while ( pPage->GetNext() && !pPage->IsFootnotePage() &&
+ !static_cast<SwPageFrame*>(pPage->GetNext())->IsEndNotePage() )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ bChgPage = true;
+ }
+
+ if ( !pPage->IsFootnotePage() )
+ {
+ SwPageDesc *pDesc = pDoc->GetFootnoteInfo().GetPageDesc( *pDoc );
+ pPage = ::InsertNewPage( *pDesc, pPage->GetUpper(),
+ !pPage->OnRightPage(), false, false, true, pPage->GetNext() );
+ bChgPage = true;
+ }
+ else
+ pPage = lcl_GetApproximateFootnotePage(false, pPage, pDoc, pAttr);
+ }
+
+ // For now, create a footnote and the corresponding content frames
+ if ( !pAttr->GetStartNode() )
+ {
+ OSL_ENSURE( false, "no footnote content." );
+ return;
+ }
+
+ // If there is already a footnote content on the column/page,
+ // another one cannot be created in a column area.
+ if( pBoss->IsInSct() && pBoss->IsColumnFrame() && !pPage->IsFootnotePage() )
+ {
+ SwSectionFrame* pSct = pBoss->FindSctFrame();
+ if( bEnd ? !pSct->IsEndnAtEnd() : !pSct->IsFootnoteAtEnd() )
+ {
+ SwFootnoteContFrame* pFootnoteCont = pSct->FindFootnoteBossFrame(!bEnd)->FindFootnoteCont();
+ if( pFootnoteCont )
+ {
+ SwFootnoteFrame* pTmp = static_cast<SwFootnoteFrame*>(pFootnoteCont->Lower());
+ if( bEnd )
+ while( pTmp && !pTmp->GetAttr()->GetFootnote().IsEndNote() )
+ pTmp = static_cast<SwFootnoteFrame*>(pTmp->GetNext());
+ if( pTmp && *pTmp < pAttr )
+ return;
+ }
+ }
+ }
+
+ SwFootnoteFrame *pNew = new SwFootnoteFrame( pDoc->GetDfltFrameFormat(), this, pRef, pAttr );
+ {
+ SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
+ ::InsertCnt_( pNew, pDoc, aIdx.GetIndex() );
+ }
+ // If the page was changed or newly created,
+ // we need to place ourselves in the first column
+ if( bChgPage )
+ {
+ SwLayoutFrame* pBody = pPage->FindBodyCont();
+ OSL_ENSURE( pBody, "AppendFootnote: NoPageBody?" );
+ if( pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ pBoss = static_cast<SwFootnoteBossFrame*>(pBody->Lower());
+ else
+ pBoss = pPage; // page if no columns exist
+ }
+ pBoss->InsertFootnote( pNew );
+ if ( pNew->GetUpper() ) // inserted or not?
+ {
+ ::RegistFlys( pNew->FindPageFrame(), pNew );
+ SwSectionFrame* pSect = FindSctFrame();
+ // The content of a FootnoteContainer in a (column) section only need to be calculated
+ // if the section stretches already to the bottom edge of the Upper.
+ if( pSect && !pSect->IsJoinLocked() && ( bEnd ? !pSect->IsEndnAtEnd() :
+ !pSect->IsFootnoteAtEnd() ) && pSect->Growable() )
+ pSect->InvalidateSize();
+ else
+ {
+ // #i49383# - disable unlock of position of
+ // lower objects during format of footnote content.
+ const bool bOldFootnoteFrameLocked( pNew->IsColLocked() );
+ pNew->ColLock();
+ pNew->KeepLockPosOfLowerObjs();
+ // #i57914# - adjust fix #i49383#
+ SwContentFrame *pCnt = pNew->ContainsContent();
+ while ( pCnt && pCnt->FindFootnoteFrame()->GetAttr() == pAttr )
+ {
+ pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ // restart format with first content
+ pCnt = pNew->ContainsContent();
+ continue;
+ }
+ }
+ pCnt = pCnt->FindNextCnt();
+ }
+ // #i49383#
+ if ( !bOldFootnoteFrameLocked )
+ {
+ pNew->ColUnlock();
+ }
+ // #i57914# - adjust fix #i49383#
+ // enable lock of lower object position before format of footnote frame.
+ pNew->UnlockPosOfLowerObjs();
+ pNew->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i57914# - adjust fix #i49383#
+ if ( !bOldFootnoteFrameLocked && !pNew->GetLower() &&
+ !pNew->IsColLocked() && !pNew->IsBackMoveLocked() &&
+ !pNew->IsDeleteForbidden() )
+ {
+ pNew->Cut();
+ SwFrame::DestroyFrame(pNew);
+ }
+ }
+ pMyPage->UpdateFootnoteNum();
+ }
+ else
+ SwFrame::DestroyFrame(pNew);
+}
+
+SwFootnoteFrame *SwFootnoteBossFrame::FindFootnote( const SwContentFrame *pRef, const SwTextFootnote *pAttr )
+{
+ // the easiest and savest way goes via the attribute
+ OSL_ENSURE( pAttr->GetStartNode(), "FootnoteAtr without StartNode." );
+ SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
+ SwContentNode *pNd = aIdx.GetNode().GetContentNode();
+ if ( !pNd )
+ pNd = pRef->GetAttrSet()->GetDoc()->
+ GetNodes().GoNextSection( &aIdx, true, false );
+ if ( !pNd )
+ return nullptr;
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
+ SwFrame* pFrame = aIter.First();
+ if( pFrame )
+ do
+ {
+ pFrame = pFrame->GetUpper();
+ // #i28500#, #i27243# Due to the endnode collector, there are
+ // SwFootnoteFrames, which are not in the layout. Therefore the
+ // bInfFootnote flags are not set correctly, and a cell of FindFootnoteFrame
+ // would return 0. Therefore we better call ImplFindFootnoteFrame().
+ SwFootnoteFrame *pFootnote = pFrame->ImplFindFootnoteFrame();
+ if ( pFootnote && pFootnote->GetRef() == pRef )
+ {
+ // The following condition becomes true, if the whole
+ // footnotecontent is a section. While no frames exist,
+ // the HiddenFlag of the section is set, this causes
+ // the GoNextSection-function leaves the footnote.
+ if( pFootnote->GetAttr() != pAttr )
+ return nullptr;
+ while ( pFootnote && pFootnote->GetMaster() )
+ pFootnote = pFootnote->GetMaster();
+ return pFootnote;
+ }
+
+ } while ( nullptr != (pFrame = aIter.Next()) );
+
+ return nullptr;
+}
+
+bool SwFootnoteBossFrame::RemoveFootnote(
+ const SwContentFrame *const pRef, const SwTextFootnote *const pAttr,
+ bool bPrep )
+{
+ bool ret(false);
+ SwFootnoteFrame *pFootnote = FindFootnote( pRef, pAttr );
+ if( pFootnote )
+ {
+ ret = true;
+ do
+ {
+ SwFootnoteFrame *pFoll = pFootnote->GetFollow();
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ pFootnote = pFoll;
+ } while ( pFootnote );
+ if( bPrep && pRef->IsFollow() )
+ {
+ OSL_ENSURE( pRef->IsTextFrame(), "NoTextFrame has Footnote?" );
+ SwTextFrame* pMaster = pRef->FindMaster();
+ if( !pMaster->IsLocked() )
+ pMaster->Prepare( PrepareHint::FootnoteInvalidationGone );
+ }
+ }
+ FindPageFrame()->UpdateFootnoteNum();
+ return ret;
+}
+
+void SwFootnoteBossFrame::ChangeFootnoteRef( const SwContentFrame *pOld, const SwTextFootnote *pAttr,
+ SwContentFrame *pNew )
+{
+ SwFootnoteFrame *pFootnote = FindFootnote( pOld, pAttr );
+ while ( pFootnote )
+ {
+ pFootnote->SetRef( pNew );
+ pFootnote = pFootnote->GetFollow();
+ }
+}
+
+/// OD 03.04.2003 #108446# - add parameter <_bCollectOnlyPreviousFootnotes> in
+/// order to control, if only footnotes, which are positioned before the
+/// footnote boss frame <this> have to be collected.
+void SwFootnoteBossFrame::CollectFootnotes( const SwContentFrame* _pRef,
+ SwFootnoteBossFrame* _pOld,
+ SwFootnoteFrames& _rFootnoteArr,
+ const bool _bCollectOnlyPreviousFootnotes )
+{
+ SwFootnoteFrame *pFootnote = _pOld->FindFirstFootnote();
+ while( !pFootnote )
+ {
+ if( _pOld->IsColumnFrame() )
+ {
+ // visit columns
+ while ( !pFootnote && _pOld->GetPrev() )
+ {
+ // Still no problem if no footnote was found yet. The loop is needed to pick up
+ // following rows in tables. In all other cases it might correct bad contexts.
+ _pOld = static_cast<SwFootnoteBossFrame*>(_pOld->GetPrev());
+ pFootnote = _pOld->FindFirstFootnote();
+ }
+ }
+ if( !pFootnote )
+ {
+ // previous page
+ SwPageFrame* pPg;
+ for ( SwFrame* pTmp = _pOld;
+ nullptr != ( pPg = static_cast<SwPageFrame*>(pTmp->FindPageFrame()->GetPrev()))
+ && pPg->IsEmptyPage() ;
+ )
+ {
+ pTmp = pPg;
+ }
+ if( !pPg )
+ return;
+
+ SwLayoutFrame* pBody = pPg->FindBodyCont();
+ if( pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ {
+ // multiple columns on one page => search last column
+ _pOld = static_cast<SwFootnoteBossFrame*>(pBody->GetLastLower());
+ }
+ else
+ _pOld = pPg; // single column page
+ pFootnote = _pOld->FindFirstFootnote();
+ }
+ }
+
+ CollectFootnotes_(_pRef, pFootnote, _rFootnoteArr, _bCollectOnlyPreviousFootnotes ? this : nullptr);
+}
+
+static void FootnoteInArr( SwFootnoteFrames& rFootnoteArr, SwFootnoteFrame* pFootnote )
+{
+ if ( rFootnoteArr.end() == std::find( rFootnoteArr.begin(), rFootnoteArr.end(), pFootnote ) )
+ rFootnoteArr.push_back( pFootnote );
+}
+
+void SwFootnoteBossFrame::CollectFootnotes_( const SwContentFrame* _pRef,
+ SwFootnoteFrame* _pFootnote,
+ SwFootnoteFrames& _rFootnoteArr,
+ const SwFootnoteBossFrame* _pRefFootnoteBossFrame)
+{
+ // Collect all footnotes referenced by pRef (attribute by attribute), combine them
+ // (the content might be divided over multiple pages) and cut them.
+
+ // For robustness, we do not log the corresponding footnotes here. If a footnote
+ // is touched twice, there might be a crash. This allows this function here to
+ // also handle corrupt layouts in some degrees (without loops or even crashes).
+ SwFootnoteFrames aNotFootnoteArr;
+
+ // here we have a footnote placed in front of the first one of the reference
+ OSL_ENSURE( !_pFootnote->GetMaster() || _pFootnote->GetRef() != _pRef, "move FollowFootnote?" );
+ while ( _pFootnote->GetMaster() )
+ _pFootnote = _pFootnote->GetMaster();
+
+ bool bFound = false;
+
+ do
+ {
+ // Search for the next footnote in this column/page so that
+ // we do not start from zero again after cutting one footnote.
+ SwFootnoteFrame *pNxtFootnote = _pFootnote;
+ while ( pNxtFootnote->GetFollow() )
+ pNxtFootnote = pNxtFootnote->GetFollow();
+ pNxtFootnote = static_cast<SwFootnoteFrame*>(pNxtFootnote->GetNext());
+
+ if ( !pNxtFootnote )
+ {
+ SwFootnoteBossFrame* pBoss = _pFootnote->FindFootnoteBossFrame();
+ SwPageFrame* pPage = pBoss->FindPageFrame();
+ do
+ {
+ lcl_NextFootnoteBoss( pBoss, pPage, false );
+ if( pBoss )
+ {
+ SwLayoutFrame* pCont = pBoss->FindFootnoteCont();
+ if( pCont )
+ {
+ pNxtFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ if( pNxtFootnote )
+ {
+ while( pNxtFootnote->GetMaster() )
+ pNxtFootnote = pNxtFootnote->GetMaster();
+ if( pNxtFootnote == _pFootnote )
+ pNxtFootnote = nullptr;
+ }
+ }
+ }
+ } while( !pNxtFootnote && pBoss );
+ }
+ else if( !pNxtFootnote->GetAttr()->GetFootnote().IsEndNote() )
+ {
+ OSL_ENSURE( !pNxtFootnote->GetMaster(), "_CollectFootnote: Master expected" );
+ while ( pNxtFootnote->GetMaster() )
+ pNxtFootnote = pNxtFootnote->GetMaster();
+ }
+ if ( pNxtFootnote == _pFootnote )
+ {
+ OSL_FAIL( "_CollectFootnote: Vicious circle" );
+ pNxtFootnote = nullptr;
+ }
+
+ // OD 03.04.2003 #108446# - determine, if found footnote has to be collected.
+ bool bCollectFoundFootnote = false;
+ // Ignore endnotes which are on a separate endnote page.
+ bool bEndNote = _pFootnote->GetAttr()->GetFootnote().IsEndNote();
+ const IDocumentSettingAccess& rSettings
+ = _pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
+ bool bContinuousEndnotes = rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES);
+ if (_pFootnote->GetRef() == _pRef && (!bEndNote || bContinuousEndnotes))
+ {
+ if (_pRefFootnoteBossFrame)
+ {
+ SwFootnoteBossFrame* pBossOfFoundFootnote = _pFootnote->FindFootnoteBossFrame( true );
+ OSL_ENSURE( pBossOfFoundFootnote,
+ "<SwFootnoteBossFrame::CollectFootnotes_(..)> - footnote boss frame of found footnote frame missing.\nWrong layout!" );
+ if ( !pBossOfFoundFootnote || // don't crash, if no footnote boss is found.
+ pBossOfFoundFootnote->IsBefore( _pRefFootnoteBossFrame )
+ )
+ {
+ bCollectFoundFootnote = true;
+ }
+ }
+ else
+ {
+ bCollectFoundFootnote = true;
+ }
+ }
+
+ if ( bCollectFoundFootnote )
+ {
+ OSL_ENSURE( !_pFootnote->GetMaster(), "move FollowFootnote?" );
+ SwFootnoteFrame *pNxt = _pFootnote->GetFollow();
+ while ( pNxt )
+ {
+ SwFrame *pCnt = pNxt->ContainsAny();
+ if ( pCnt )
+ {
+ // destroy the follow on the way as it is empty
+ do
+ { SwFrame *pNxtCnt = pCnt->GetNext();
+ pCnt->Cut();
+ pCnt->Paste( _pFootnote );
+ pCnt = pNxtCnt;
+ } while ( pCnt );
+ }
+ else
+ {
+ OSL_ENSURE( !pNxt, "footnote without content?" );
+ pNxt->Cut();
+ SwFrame::DestroyFrame(pNxt);
+ }
+ pNxt = _pFootnote->GetFollow();
+ }
+ _pFootnote->Cut();
+ FootnoteInArr( _rFootnoteArr, _pFootnote );
+ bFound = true;
+ }
+ else
+ {
+ FootnoteInArr( aNotFootnoteArr, _pFootnote );
+ if( bFound )
+ break;
+ }
+ if ( pNxtFootnote &&
+ _rFootnoteArr.end() == std::find( _rFootnoteArr.begin(), _rFootnoteArr.end(), pNxtFootnote ) &&
+ aNotFootnoteArr.end() == std::find( aNotFootnoteArr.begin(), aNotFootnoteArr.end(), pNxtFootnote ) )
+ _pFootnote = pNxtFootnote;
+ else
+ break;
+ }
+ while ( _pFootnote );
+}
+
+void SwFootnoteBossFrame::MoveFootnotes_( SwFootnoteFrames &rFootnoteArr, bool bCalc )
+{
+ // All footnotes referenced by pRef need to be moved
+ // to a new position (based on the new column/page)
+ const sal_uInt16 nMyNum = FindPageFrame()->GetPhyPageNum();
+ const sal_uInt16 nMyCol = lcl_ColumnNum( this );
+ SwRectFnSet aRectFnSet(this);
+
+ // #i21478# - keep last inserted footnote in order to
+ // format the content of the following one.
+ SwFootnoteFrame* pLastInsertedFootnote = nullptr;
+ for (SwFootnoteFrame* pFootnote : rFootnoteArr)
+ {
+ SwFootnoteBossFrame* pRefBoss(pFootnote->GetRef()->FindFootnoteBossFrame(
+ !pFootnote->GetAttr()->GetFootnote().IsEndNote()));
+ if( pRefBoss != this )
+ {
+ const sal_uInt16 nRefNum = pRefBoss->FindPageFrame()->GetPhyPageNum();
+ const sal_uInt16 nRefCol = lcl_ColumnNum( this );
+ if( nRefNum < nMyNum || ( nRefNum == nMyNum && nRefCol <= nMyCol ) )
+ pRefBoss = this;
+ }
+ pRefBoss->InsertFootnote( pFootnote );
+
+ if ( pFootnote->GetUpper() ) // robust, e.g. with duplicates
+ {
+ // First condense the content so that footnote frames that do not fit on the page
+ // do not do too much harm (Loop 66312). So, the footnote content first grows as
+ // soon as the content gets formatted and it is sure that it fits on the page.
+ SwFrame *pCnt = pFootnote->ContainsAny();
+ while( pCnt )
+ {
+ if( pCnt->IsLayoutFrame() )
+ {
+ SwFrame* pTmp = static_cast<SwLayoutFrame*>(pCnt)->ContainsAny();
+ while( pTmp && static_cast<SwLayoutFrame*>(pCnt)->IsAnLower( pTmp ) )
+ {
+ pTmp->Prepare( PrepareHint::FootnoteMove );
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pTmp);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
+ aRectFnSet.SetHeight(aPrt, 0);
+
+ pTmp = pTmp->FindNext();
+ }
+ }
+ else
+ {
+ pCnt->Prepare( PrepareHint::FootnoteMove );
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCnt);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pCnt);
+ aRectFnSet.SetHeight(aPrt, 0);
+
+ pCnt = pCnt->GetNext();
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFootnote);
+ aRectFnSet.SetHeight(aFrm, 0);
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFootnote);
+ aRectFnSet.SetHeight(aPrt, 0);
+ }
+
+ pFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ pFootnote->GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
+
+ if( bCalc )
+ {
+ SwTextFootnote *pAttr = pFootnote->GetAttr();
+ pCnt = pFootnote->ContainsAny();
+ bool bUnlock = !pFootnote->IsBackMoveLocked();
+ pFootnote->LockBackMove();
+
+ // #i49383# - disable unlock of position of
+ // lower objects during format of footnote content.
+ pFootnote->KeepLockPosOfLowerObjs();
+ // #i57914# - adjust fix #i49383#
+
+ while ( pCnt && pCnt->FindFootnoteFrame()->GetAttr() == pAttr )
+ {
+ pCnt->InvalidatePos_();
+ pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ // restart format with first content
+ pCnt = pFootnote->ContainsAny();
+ continue;
+ }
+ }
+ if( pCnt->IsSctFrame() )
+ {
+ // If the area is not empty, iterate also over the content
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
+ if( pTmp )
+ pCnt = pTmp;
+ else
+ pCnt = pCnt->FindNext();
+ }
+ else
+ pCnt = pCnt->FindNext();
+ }
+ if( bUnlock )
+ {
+ pFootnote->UnlockBackMove();
+ if( !pFootnote->ContainsAny() && !pFootnote->IsColLocked() )
+ {
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ // #i21478#
+ pFootnote = nullptr;
+ }
+ }
+ // #i49383#
+ if ( pFootnote )
+ {
+ // #i57914# - adjust fix #i49383#
+ // enable lock of lower object position before format of footnote frame.
+ pFootnote->UnlockPosOfLowerObjs();
+ pFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ }
+ }
+ }
+ else
+ {
+ OSL_ENSURE( !pFootnote->GetMaster() && !pFootnote->GetFollow(),
+ "DelFootnote and Master/Follow?" );
+ SwFrame::DestroyFrame(pFootnote);
+ // #i21478#
+ pFootnote = nullptr;
+ }
+
+ // #i21478#
+ if ( pFootnote )
+ {
+ pLastInsertedFootnote = pFootnote;
+ }
+ }
+
+ // #i21478# - format content of footnote following
+ // the new inserted ones.
+ if ( !(bCalc && pLastInsertedFootnote) )
+ return;
+
+ if ( !pLastInsertedFootnote->GetNext() )
+ return;
+
+ SwFootnoteFrame* pNextFootnote = static_cast<SwFootnoteFrame*>(pLastInsertedFootnote->GetNext());
+ SwTextFootnote* pAttr = pNextFootnote->GetAttr();
+ SwFrame* pCnt = pNextFootnote->ContainsAny();
+
+ bool bUnlock = !pNextFootnote->IsBackMoveLocked();
+ pNextFootnote->LockBackMove();
+ // #i49383# - disable unlock of position of
+ // lower objects during format of footnote content.
+ pNextFootnote->KeepLockPosOfLowerObjs();
+ // #i57914# - adjust fix #i49383#
+
+ while ( pCnt && pCnt->FindFootnoteFrame()->GetAttr() == pAttr )
+ {
+ pCnt->InvalidatePos_();
+ pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ // restart format with first content
+ pCnt = pNextFootnote->ContainsAny();
+ continue;
+ }
+ }
+ if( pCnt->IsSctFrame() )
+ {
+ // If the area is not empty, iterate also over the content
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
+ if( pTmp )
+ pCnt = pTmp;
+ else
+ pCnt = pCnt->FindNext();
+ }
+ else
+ pCnt = pCnt->FindNext();
+ }
+ if( bUnlock )
+ {
+ pNextFootnote->UnlockBackMove();
+ }
+ // #i49383#
+ // #i57914# - adjust fix #i49383#
+ // enable lock of lower object position before format of footnote frame.
+ pNextFootnote->UnlockPosOfLowerObjs();
+ pNextFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
+}
+
+void SwFootnoteBossFrame::MoveFootnotes( const SwContentFrame *pSrc, SwContentFrame *pDest,
+ SwTextFootnote const *pAttr )
+{
+ if( ( GetFormat()->GetDoc()->GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER &&
+ (!GetUpper()->IsSctFrame() || !static_cast<SwSectionFrame*>(GetUpper())->IsFootnoteAtEnd()))
+ || pAttr->GetFootnote().IsEndNote() )
+ return;
+
+ OSL_ENSURE( this == pSrc->FindFootnoteBossFrame( true ),
+ "SwPageFrame::MoveFootnotes: source frame isn't on that FootnoteBoss" );
+
+ SwFootnoteFrame *pFootnote = FindFirstFootnote();
+ if( !pFootnote )
+ return;
+
+ ChangeFootnoteRef( pSrc, pAttr, pDest );
+ SwFootnoteBossFrame *pDestBoss = pDest->FindFootnoteBossFrame( true );
+ OSL_ENSURE( pDestBoss, "+SwPageFrame::MoveFootnotes: no destination boss" );
+ if( !pDestBoss ) // robust
+ return;
+
+ SwFootnoteFrames aFootnoteArr;
+ SwFootnoteBossFrame::CollectFootnotes_(pDest, pFootnote, aFootnoteArr, nullptr);
+ if ( aFootnoteArr.empty() )
+ return;
+
+ pDestBoss->MoveFootnotes_( aFootnoteArr, true );
+ SwPageFrame* pSrcPage = FindPageFrame();
+ SwPageFrame* pDestPage = pDestBoss->FindPageFrame();
+ // update FootnoteNum only at page change
+ if( pSrcPage != pDestPage )
+ {
+ if( pSrcPage->GetPhyPageNum() > pDestPage->GetPhyPageNum() )
+ pSrcPage->UpdateFootnoteNum();
+ pDestPage->UpdateFootnoteNum();
+ }
+}
+
+void SwFootnoteBossFrame::RearrangeFootnotes( const SwTwips nDeadLine, const bool bLock,
+ const SwTextFootnote *pAttr )
+{
+ // Format all footnotes of a column/page so that they might change the column/page.
+
+ SwSaveFootnoteHeight aSave( this, nDeadLine );
+ SwFootnoteFrame *pFootnote = FindFirstFootnote();
+ if( pFootnote && pFootnote->GetPrev() && bLock )
+ {
+ SwFootnoteFrame* pFirst = static_cast<SwFootnoteFrame*>(pFootnote->GetUpper()->Lower());
+ SwFrame* pContent = pFirst->ContainsAny();
+ if( pContent )
+ {
+ bool bUnlock = !pFirst->IsBackMoveLocked();
+ pFirst->LockBackMove();
+ pFirst->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ pContent->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pContent->IsTextFrame() && pContent->isFrameAreaDefinitionValid() )
+ {
+ SwObjectFormatter::FormatObjsAtFrame( *pContent,
+ *(pContent->FindPageFrame()) );
+ }
+ if( bUnlock )
+ pFirst->UnlockBackMove();
+ }
+ pFootnote = FindFirstFootnote();
+ }
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ const sal_uLong nFootnotePos = pAttr ? ::lcl_FindFootnotePos( pDoc, pAttr ) : 0;
+ SwFrame *pCnt = pFootnote ? pFootnote->ContainsAny() : nullptr;
+ if ( !pCnt )
+ return;
+
+ bool bMore = true;
+ bool bStart = pAttr == nullptr; // If no attribute is given, process all
+ // #i49383# - disable unlock of position of
+ // lower objects during format of footnote and footnote content.
+ SwFootnoteFrame* pLastFootnoteFrame( nullptr );
+ // footnote frame needs to be locked, if <bLock> isn't set.
+ bool bUnlockLastFootnoteFrame( false );
+ do
+ {
+ if( !bStart )
+ bStart = ::lcl_FindFootnotePos( pDoc, pCnt->FindFootnoteFrame()->GetAttr() )
+ == nFootnotePos;
+ if( bStart )
+ {
+ pCnt->InvalidatePos_();
+ pCnt->InvalidateSize_();
+ pCnt->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ SwFootnoteFrame* pFootnoteFrame = pCnt->FindFootnoteFrame();
+ // #i49383#
+ if ( pFootnoteFrame != pLastFootnoteFrame )
+ {
+ if ( pLastFootnoteFrame )
+ {
+ if ( !bLock && bUnlockLastFootnoteFrame )
+ {
+ pLastFootnoteFrame->ColUnlock();
+ }
+ // #i57914# - adjust fix #i49383#
+ // enable lock of lower object position before format of footnote frame.
+ pLastFootnoteFrame->UnlockPosOfLowerObjs();
+ pLastFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ if ( !bLock && bUnlockLastFootnoteFrame &&
+ !pLastFootnoteFrame->GetLower() &&
+ !pLastFootnoteFrame->IsColLocked() &&
+ !pLastFootnoteFrame->IsBackMoveLocked() &&
+ !pLastFootnoteFrame->IsDeleteForbidden() )
+ {
+ pLastFootnoteFrame->Cut();
+ SwFrame::DestroyFrame(pLastFootnoteFrame);
+ pLastFootnoteFrame = nullptr;
+ }
+ }
+ if ( !bLock )
+ {
+ bUnlockLastFootnoteFrame = !pFootnoteFrame->IsColLocked();
+ pFootnoteFrame->ColLock();
+ }
+ pFootnoteFrame->KeepLockPosOfLowerObjs();
+ pLastFootnoteFrame = pFootnoteFrame;
+ }
+ // OD 30.10.2002 #97265# - invalidate position of footnote
+ // frame, if it's below its footnote container, in order to
+ // assure its correct position, probably calculating its previous
+ // footnote frames.
+ {
+ SwRectFnSet aRectFnSet(this);
+ SwFrame* pFootnoteContFrame = pFootnoteFrame->GetUpper();
+ if ( aRectFnSet.TopDist(pFootnoteFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*pFootnoteContFrame)) > 0 )
+ {
+ pFootnoteFrame->InvalidatePos_();
+ }
+ }
+ if ( bLock )
+ {
+ bool bUnlock = !pFootnoteFrame->IsBackMoveLocked();
+ pFootnoteFrame->LockBackMove();
+ pFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ SwFrameDeleteGuard aDeleteGuard(pFootnote);
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ // restart format with first content
+ pCnt = pFootnote ? pFootnote->ContainsAny() : nullptr;
+ if (!pCnt)
+ bMore = false;
+ continue;
+ }
+ }
+ if( bUnlock )
+ {
+ pFootnoteFrame->UnlockBackMove();
+ if( !pFootnoteFrame->Lower() &&
+ !pFootnoteFrame->IsColLocked() )
+ {
+ // #i49383#
+ OSL_ENSURE( pLastFootnoteFrame == pFootnoteFrame,
+ "<SwFootnoteBossFrame::RearrangeFootnotes(..)> - <pLastFootnoteFrame> != <pFootnoteFrame>" );
+ pLastFootnoteFrame = nullptr;
+ pFootnoteFrame->Cut();
+ SwFrame::DestroyFrame(pFootnoteFrame);
+ if (pFootnote == pFootnoteFrame)
+ pFootnote = nullptr;
+ }
+ }
+ }
+ else
+ {
+ pFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ // restart format with first content
+ pCnt = pFootnote->ContainsAny();
+ continue;
+ }
+ }
+ }
+ }
+ SwSectionFrame *pDel = nullptr;
+ if( pCnt->IsSctFrame() )
+ {
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
+ if( pTmp )
+ {
+ pCnt = pTmp;
+ continue;
+ }
+ pDel = static_cast<SwSectionFrame*>(pCnt);
+ }
+ if ( pCnt->GetNext() )
+ pCnt = pCnt->GetNext();
+ else
+ {
+ pCnt = pCnt->FindNext();
+ if ( pCnt )
+ {
+ SwFootnoteFrame* pFootnoteFrame = pCnt->FindFootnoteFrame();
+ if( pFootnoteFrame->GetRef()->FindFootnoteBossFrame(
+ pFootnoteFrame->GetAttr()->GetFootnote().IsEndNote() ) != this )
+ bMore = false;
+ }
+ else
+ bMore = false;
+ }
+ if( pDel )
+ {
+ bool bUnlockLastFootnoteFrameGuard = pLastFootnoteFrame && !pLastFootnoteFrame->IsColLocked();
+ if (bUnlockLastFootnoteFrameGuard)
+ pLastFootnoteFrame->ColLock();
+ pDel->Cut();
+ if (bUnlockLastFootnoteFrameGuard)
+ pLastFootnoteFrame->ColUnlock();
+ SwFrame::DestroyFrame(pDel);
+ }
+ if ( bMore )
+ {
+ // Go not further than to the provided footnote (if given)
+ if ( pAttr &&
+ (::lcl_FindFootnotePos( pDoc,
+ pCnt->FindFootnoteFrame()->GetAttr()) > nFootnotePos ) )
+ bMore = false;
+ }
+ } while ( bMore );
+ // #i49383#
+ if ( !pLastFootnoteFrame )
+ return;
+
+ if ( !bLock && bUnlockLastFootnoteFrame )
+ {
+ pLastFootnoteFrame->ColUnlock();
+ }
+ // #i57914# - adjust fix #i49383#
+ // enable lock of lower object position before format of footnote frame.
+ pLastFootnoteFrame->UnlockPosOfLowerObjs();
+ pLastFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ if ( !bLock && bUnlockLastFootnoteFrame &&
+ !pLastFootnoteFrame->GetLower() &&
+ !pLastFootnoteFrame->IsColLocked() &&
+ !pLastFootnoteFrame->IsBackMoveLocked() &&
+ !pLastFootnoteFrame->IsDeleteForbidden() )
+ {
+ pLastFootnoteFrame->Cut();
+ SwFrame::DestroyFrame(pLastFootnoteFrame);
+ }
+}
+
+void SwPageFrame::UpdateFootnoteNum()
+{
+ // page numbering only if set at the document
+ if ( GetFormat()->GetDoc()->GetFootnoteInfo().m_eNum != FTNNUM_PAGE )
+ return;
+
+ SwLayoutFrame* pBody = FindBodyCont();
+ if( !pBody || !pBody->Lower() )
+ return;
+
+ SwContentFrame* pContent = pBody->ContainsContent();
+ sal_uInt16 nNum = 0;
+
+ while( pContent && pContent->FindPageFrame() == this )
+ {
+ if( static_cast<SwTextFrame*>(pContent)->HasFootnote() )
+ {
+ SwFootnoteBossFrame* pBoss = pContent->FindFootnoteBossFrame( true );
+ if( pBoss->GetUpper()->IsSctFrame() &&
+ static_cast<SwSectionFrame*>(pBoss->GetUpper())->IsOwnFootnoteNum() )
+ pContent = static_cast<SwSectionFrame*>(pBoss->GetUpper())->FindLastContent();
+ else
+ {
+ SwFootnoteFrame* pFootnote = const_cast<SwFootnoteFrame*>(pBoss->FindFirstFootnote( pContent ));
+ while( pFootnote )
+ {
+ SwTextFootnote* pTextFootnote = pFootnote->GetAttr();
+ if( !pTextFootnote->GetFootnote().IsEndNote() &&
+ pTextFootnote->GetFootnote().GetNumStr().isEmpty() &&
+ !pFootnote->GetMaster())
+ {
+ // sw_redlinehide: the layout can only keep one number
+ // up to date; depending on its setting, this is either
+ // the non-hidden or the hidden number; the other
+ // number will simply be preserved as-is (so in case
+ // there are 2 layouts, maybe both can be updated...)
+ ++nNum;
+ sal_uInt16 const nOldNum(pTextFootnote->GetFootnote().GetNumber());
+ sal_uInt16 const nOldNumRLHidden(pTextFootnote->GetFootnote().GetNumberRLHidden());
+ if (getRootFrame()->IsHideRedlines())
+ {
+ if (nNum != nOldNumRLHidden)
+ {
+ pTextFootnote->SetNumber(nOldNum, nNum, OUString());
+ }
+ }
+ else
+ {
+ if (nNum != nOldNum)
+ {
+ pTextFootnote->SetNumber(nNum, nOldNumRLHidden, OUString());
+ }
+ }
+ }
+ if ( pFootnote->GetNext() )
+ pFootnote = static_cast<SwFootnoteFrame*>(pFootnote->GetNext());
+ else
+ {
+ SwFootnoteBossFrame* pTmpBoss = pFootnote->FindFootnoteBossFrame( true );
+ if( pTmpBoss )
+ {
+ SwPageFrame* pPage = pTmpBoss->FindPageFrame();
+ pFootnote = nullptr;
+ lcl_NextFootnoteBoss( pTmpBoss, pPage, false );
+ SwFootnoteContFrame *pCont = pTmpBoss ? pTmpBoss->FindNearestFootnoteCont() : nullptr;
+ if ( pCont )
+ pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ }
+ }
+ if( pFootnote && pFootnote->GetRef() != pContent )
+ pFootnote = nullptr;
+ }
+ }
+ }
+ pContent = pContent->FindNextCnt();
+ }
+}
+
+void SwFootnoteBossFrame::SetFootnoteDeadLine( const SwTwips nDeadLine )
+{
+ SwFrame *pBody = FindBodyCont();
+ pBody->Calc(getRootFrame()->GetCurrShell()->GetOut());
+
+ SwFrame *pCont = FindFootnoteCont();
+ const SwTwips nMax = m_nMaxFootnoteHeight;// current should exceed MaxHeight
+ SwRectFnSet aRectFnSet(this);
+ if ( pCont )
+ {
+ pCont->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ m_nMaxFootnoteHeight = -aRectFnSet.BottomDist( pCont->getFrameArea(), nDeadLine );
+ }
+ else
+ m_nMaxFootnoteHeight = -aRectFnSet.BottomDist( pBody->getFrameArea(), nDeadLine );
+
+ const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ m_nMaxFootnoteHeight += pBody->Grow( LONG_MAX, true );
+ if ( IsInSct() )
+ m_nMaxFootnoteHeight += FindSctFrame()->Grow( LONG_MAX, true );
+
+ if ( m_nMaxFootnoteHeight < 0 )
+ m_nMaxFootnoteHeight = 0;
+ if ( nMax != LONG_MAX && m_nMaxFootnoteHeight > nMax )
+ m_nMaxFootnoteHeight = nMax;
+}
+
+SwTwips SwFootnoteBossFrame::GetVarSpace() const
+{
+ // To not fall below 20% of the page height
+ // (in contrast to MSOffice where footnotes can fill a whole column/page)
+
+ const SwPageFrame* pPg = FindPageFrame();
+ OSL_ENSURE( pPg || IsInSct(), "Footnote lost page" );
+
+ const SwFrame *pBody = FindBodyCont();
+ SwTwips nRet;
+ if( pBody )
+ {
+ SwRectFnSet aRectFnSet(this);
+ if( IsInSct() )
+ {
+ nRet = 0;
+ SwTwips nTmp = aRectFnSet.YDiff( aRectFnSet.GetPrtTop(*pBody),
+ aRectFnSet.GetTop(getFrameArea()) );
+ const SwSectionFrame* pSect = FindSctFrame();
+ // Endnotes in a ftncontainer causes a deadline:
+ // the bottom of the last contentfrm
+ if( pSect->IsEndnAtEnd() ) // endnotes allowed?
+ {
+ OSL_ENSURE( !Lower() || !Lower()->GetNext() || Lower()->GetNext()->
+ IsFootnoteContFrame(), "FootnoteContainer expected" );
+ const SwFootnoteContFrame* pCont = Lower() ?
+ static_cast<const SwFootnoteContFrame*>(Lower()->GetNext()) : nullptr;
+ if( pCont )
+ {
+ const SwFootnoteFrame* pFootnote = static_cast<const SwFootnoteFrame*>(pCont->Lower());
+ while( pFootnote)
+ {
+ if( pFootnote->GetAttr()->GetFootnote().IsEndNote() )
+ { // endnote found
+ const SwFrame* pFrame = static_cast<const SwLayoutFrame*>(Lower())->Lower();
+ if( pFrame )
+ {
+ while( pFrame->GetNext() )
+ pFrame = pFrame->GetNext(); // last cntntfrm
+ nTmp += aRectFnSet.YDiff(
+ aRectFnSet.GetTop(getFrameArea()),
+ aRectFnSet.GetBottom(pFrame->getFrameArea()) );
+ }
+ break;
+ }
+ pFootnote = static_cast<const SwFootnoteFrame*>(pFootnote->GetNext());
+ }
+ }
+ }
+ if( nTmp < nRet )
+ nRet = nTmp;
+ }
+ else
+ nRet = - aRectFnSet.GetHeight(pPg->getFramePrintArea())/5;
+ nRet += aRectFnSet.GetHeight(pBody->getFrameArea());
+ if( nRet < 0 )
+ nRet = 0;
+ }
+ else
+ nRet = 0;
+ if ( IsPageFrame() )
+ {
+ const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ nRet += BROWSE_HEIGHT - getFrameArea().Height();
+ }
+ return nRet;
+}
+
+/** Obtain if pFrame's size adjustment should be processed
+ *
+ * For a page frame of columns directly below the page AdjustNeighbourhood() needs
+ * to be called, or Grow()/ Shrink() for frame columns respectively.
+ *
+ * A column section is special, since if there is a footnote container in a column
+ * and those footnotes are not collected, it is handled like a page frame.
+ *
+ * @see AdjustNeighbourhood()
+ * @see Grow()
+ * @see Shrink()
+ */
+SwNeighbourAdjust SwFootnoteBossFrame::NeighbourhoodAdjustment_() const
+{
+ SwNeighbourAdjust nRet = SwNeighbourAdjust::OnlyAdjust;
+ if( GetUpper() && !GetUpper()->IsPageBodyFrame() )
+ {
+ // column sections need grow/shrink
+ if( GetUpper()->IsFlyFrame() )
+ nRet = SwNeighbourAdjust::GrowShrink;
+ else
+ {
+ OSL_ENSURE( GetUpper()->IsSctFrame(), "NeighbourhoodAdjustment: Unexpected Upper" );
+ if( !GetNext() && !GetPrev() )
+ nRet = SwNeighbourAdjust::GrowAdjust; // section with a single column (FootnoteAtEnd)
+ else
+ {
+ const SwFrame* pTmp = Lower();
+ OSL_ENSURE( pTmp, "NeighbourhoodAdjustment: Missing Lower()" );
+ if( !pTmp->GetNext() )
+ nRet = SwNeighbourAdjust::GrowShrink;
+ else if( !GetUpper()->IsColLocked() )
+ nRet = SwNeighbourAdjust::AdjustGrow;
+ OSL_ENSURE( !pTmp->GetNext() || pTmp->GetNext()->IsFootnoteContFrame(),
+ "NeighbourhoodAdjustment: Who's that guy?" );
+ }
+ }
+ }
+ return nRet;
+}
+
+void SwPageFrame::SetColMaxFootnoteHeight()
+{
+ SwLayoutFrame *pBody = FindBodyCont();
+ if( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ {
+ SwColumnFrame* pCol = static_cast<SwColumnFrame*>(pBody->Lower());
+ do
+ {
+ pCol->SetMaxFootnoteHeight( GetMaxFootnoteHeight() );
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ } while ( pCol );
+ }
+}
+
+bool SwLayoutFrame::MoveLowerFootnotes( SwContentFrame *pStart, SwFootnoteBossFrame *pOldBoss,
+ SwFootnoteBossFrame *pNewBoss, const bool bFootnoteNums )
+{
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ if ( pDoc->GetFootnoteIdxs().empty() )
+ return false;
+ if( pDoc->GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER &&
+ ( !IsInSct() || !FindSctFrame()->IsFootnoteAtEnd() ) )
+ return true;
+
+ if ( !pNewBoss )
+ pNewBoss = FindFootnoteBossFrame( true );
+ if ( pNewBoss == pOldBoss )
+ return false;
+
+ bool bMoved = false;
+ if( !pStart )
+ pStart = ContainsContent();
+
+ SwFootnoteFrames aFootnoteArr;
+
+ while ( IsAnLower( pStart ) )
+ {
+ if ( static_cast<SwTextFrame*>(pStart)->HasFootnote() )
+ {
+ // OD 03.04.2003 #108446# - To avoid unnecessary moves of footnotes
+ // use new parameter <_bCollectOnlyPreviousFootnote> (4th parameter of
+ // method <SwFootnoteBossFrame::CollectFootnote(..)>) to control, that only
+ // footnotes have to be collected, that are positioned before the
+ // new dedicated footnote boss frame.
+ pNewBoss->CollectFootnotes( pStart, pOldBoss, aFootnoteArr, true );
+ }
+ pStart = pStart->GetNextContentFrame();
+ }
+
+ OSL_ENSURE( pOldBoss->IsInSct() == pNewBoss->IsInSct(),
+ "MoveLowerFootnotes: Section confusion" );
+ std::unique_ptr<SwFootnoteFrames> pFootnoteArr;
+ SwLayoutFrame* pNewChief = nullptr;
+ SwLayoutFrame* pOldChief = nullptr;
+
+ bool bFoundCandidate = false;
+ if (pStart && pOldBoss->IsInSct())
+ {
+ pOldChief = pOldBoss->FindSctFrame();
+ pNewChief = pNewBoss->FindSctFrame();
+ bFoundCandidate = pOldChief != pNewChief;
+ }
+
+ if (bFoundCandidate)
+ {
+ pFootnoteArr.reset(new SwFootnoteFrames);
+ pOldChief = pOldBoss->FindFootnoteBossFrame( true );
+ pNewChief = pNewBoss->FindFootnoteBossFrame( true );
+ while( pOldChief->IsAnLower( pStart ) )
+ {
+ if ( static_cast<SwTextFrame*>(pStart)->HasFootnote() )
+ static_cast<SwFootnoteBossFrame*>(pNewChief)->CollectFootnotes( pStart,
+ pOldBoss, *pFootnoteArr );
+ pStart = pStart->GetNextContentFrame();
+ }
+ if( pFootnoteArr->empty() )
+ {
+ pFootnoteArr.reset();
+ }
+ }
+ else
+ pFootnoteArr = nullptr;
+
+ if ( !aFootnoteArr.empty() || pFootnoteArr )
+ {
+ if( !aFootnoteArr.empty() )
+ pNewBoss->MoveFootnotes_( aFootnoteArr, true );
+ if( pFootnoteArr )
+ {
+ assert(pNewChief);
+ static_cast<SwFootnoteBossFrame*>(pNewChief)->MoveFootnotes_( *pFootnoteArr, true );
+ pFootnoteArr.reset();
+ }
+ bMoved = true;
+
+ // update FootnoteNum only at page change
+ if ( bFootnoteNums )
+ {
+ SwPageFrame* pOldPage = pOldBoss->FindPageFrame();
+ SwPageFrame* pNewPage =pNewBoss->FindPageFrame();
+ if( pOldPage != pNewPage )
+ {
+ pOldPage->UpdateFootnoteNum();
+ pNewPage->UpdateFootnoteNum();
+ }
+ }
+ }
+ return bMoved;
+}
+
+/// Return value guarantees that a new page was not created. See SwFlowFrame::MoveFwd.
+bool SwContentFrame::MoveFootnoteCntFwd( bool bMakePage, SwFootnoteBossFrame *pOldBoss )
+{
+ OSL_ENSURE( IsInFootnote(), "no footnote." );
+ SwLayoutFrame *pFootnote = FindFootnoteFrame();
+
+ // The first paragraph in the first footnote in the first column in the
+ // sectionfrm at the top of the page has not to move forward, if the
+ // columnbody is empty.
+ if( pOldBoss->IsInSct() && !pOldBoss->GetIndPrev() && !GetIndPrev() &&
+ !pFootnote->GetPrev() )
+ {
+ SwLayoutFrame* pBody = pOldBoss->FindBodyCont();
+ if( !pBody || !pBody->Lower() )
+ return true;
+ }
+
+ //fix(9538): if the footnote has neighbors behind itself, remove them temporarily
+ SwLayoutFrame *pNxt = static_cast<SwLayoutFrame*>(pFootnote->GetNext());
+ SwLayoutFrame *pLst = nullptr;
+ while ( pNxt )
+ {
+ while ( pNxt->GetNext() )
+ pNxt = static_cast<SwLayoutFrame*>(pNxt->GetNext());
+ if ( pNxt == pLst )
+ pNxt = nullptr;
+ else
+ { pLst = pNxt;
+ SwContentFrame *pCnt = pNxt->ContainsContent();
+ if( pCnt )
+ pCnt->MoveFootnoteCntFwd( true, pOldBoss );
+ pNxt = static_cast<SwLayoutFrame*>(pFootnote->GetNext());
+ }
+ }
+
+ bool bSamePage = true;
+ SwLayoutFrame *pNewUpper =
+ GetLeaf( bMakePage ? MAKEPAGE_INSERT : MAKEPAGE_NONE, true );
+
+ if ( pNewUpper )
+ {
+ SwFootnoteBossFrame * const pNewBoss = pNewUpper->FindFootnoteBossFrame();
+ // Are we changing the column/page?
+ bool bSameBoss = pNewBoss == pOldBoss;
+ if ( !bSameBoss )
+ {
+ bSamePage = pOldBoss->FindPageFrame() == pNewBoss->FindPageFrame(); // page change?
+ pNewUpper->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ }
+
+ // The layout leaf of the footnote is either a footnote container or a footnote.
+ // If it is a footnote and it has the same footnote reference like the old Upper,
+ // then move the content inside of it.
+ // If it is a container or the reference differs, create a new footnote and add
+ // it into the container.
+ // Create also a SectionFrame if currently in an area inside a footnote.
+ SwFootnoteFrame* pTmpFootnote = pNewUpper->IsFootnoteFrame() ? static_cast<SwFootnoteFrame*>(pNewUpper) : nullptr;
+ if (!pTmpFootnote)
+ {
+ assert(pNewUpper->IsFootnoteContFrame() && "New Upper not a FootnoteCont");
+ SwFootnoteContFrame *pCont = static_cast<SwFootnoteContFrame*>(pNewUpper);
+ pTmpFootnote = SwFootnoteContFrame::AppendChained(this, true);
+ SwFrame* pNx = pCont->Lower();
+ if( pNx && pTmpFootnote->GetAttr()->GetFootnote().IsEndNote() )
+ while(pNx && !static_cast<SwFootnoteFrame*>(pNx)->GetAttr()->GetFootnote().IsEndNote())
+ pNx = pNx->GetNext();
+ pTmpFootnote->Paste( pCont, pNx );
+ pTmpFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ }
+ OSL_ENSURE( pTmpFootnote->GetAttr() == FindFootnoteFrame()->GetAttr(), "Wrong Footnote!" );
+ // areas inside of footnotes get a special treatment
+ SwLayoutFrame *pNewUp = pTmpFootnote;
+ if( IsInSct() )
+ {
+ SwSectionFrame* pSect = FindSctFrame();
+ // area inside of a footnote (or only footnote in an area)?
+ if( pSect->IsInFootnote() )
+ {
+ if( pTmpFootnote->Lower() && pTmpFootnote->Lower()->IsSctFrame() &&
+ pSect->GetFollow() == static_cast<SwSectionFrame*>(pTmpFootnote->Lower()) )
+ pNewUp = static_cast<SwSectionFrame*>(pTmpFootnote->Lower());
+ else
+ {
+ pNewUp = new SwSectionFrame( *pSect, false );
+ pNewUp->InsertBefore( pTmpFootnote, pTmpFootnote->Lower() );
+ static_cast<SwSectionFrame*>(pNewUp)->Init();
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNewUp);
+ aFrm.Pos() = pTmpFootnote->getFrameArea().Pos();
+ aFrm.Pos().AdjustY(1 ); // for notifications
+ }
+
+ // If the section frame has a successor then the latter needs
+ // to be moved behind the new Follow of the section frame.
+ SwFrame* pTmp = pSect->GetNext();
+ if( pTmp )
+ {
+ SwFlowFrame* pTmpNxt;
+ if( pTmp->IsContentFrame() )
+ pTmpNxt = static_cast<SwContentFrame*>(pTmp);
+ else if( pTmp->IsSctFrame() )
+ pTmpNxt = static_cast<SwSectionFrame*>(pTmp);
+ else
+ {
+ OSL_ENSURE( pTmp->IsTabFrame(), "GetNextSctLeaf: Wrong Type" );
+ pTmpNxt = static_cast<SwTabFrame*>(pTmp);
+ }
+ // we will dereference pNewUp in the following MoveSubTree call
+ // so it certainly should not be deleted before that
+ SwFrameDeleteGuard aDeleteGuard(pNewUp);
+ pTmpNxt->MoveSubTree( pTmpFootnote, pNewUp->GetNext() );
+ }
+ }
+ }
+ }
+
+ MoveSubTree( pNewUp, pNewUp->Lower() );
+
+ if( !bSameBoss )
+ Prepare( PrepareHint::BossChanged );
+ }
+ return bSamePage;
+}
+
+SwSaveFootnoteHeight::SwSaveFootnoteHeight( SwFootnoteBossFrame *pBs, const SwTwips nDeadLine ) :
+ aGuard(pBs),
+ pBoss( pBs ),
+ nOldHeight( pBs->GetMaxFootnoteHeight() )
+{
+ pBoss->SetFootnoteDeadLine( nDeadLine );
+ nNewHeight = pBoss->GetMaxFootnoteHeight();
+}
+
+SwSaveFootnoteHeight::~SwSaveFootnoteHeight()
+{
+ // If somebody tweaked the deadline meanwhile, we let it happen
+ if ( nNewHeight == pBoss->GetMaxFootnoteHeight() )
+ pBoss->m_nMaxFootnoteHeight = nOldHeight;
+}
+
+#ifdef DBG_UTIL
+//JP 15.10.2001: in a non pro version test if the attribute has the same
+// meaning which his reference is
+
+// Normally, the pRef member and the GetRefFromAttr() result has to be
+// identically. Sometimes footnote will be moved from a master to its follow,
+// but the GetRef() is called first, so we have to ignore a master/follow
+// mismatch.
+
+const SwContentFrame* SwFootnoteFrame::GetRef() const
+{
+ const SwContentFrame* pRefAttr = GetRefFromAttr();
+ // check consistency: access to deleted frame?
+ assert(mpReference == pRefAttr || mpReference->IsAnFollow(pRefAttr)
+ || pRefAttr->IsAnFollow(mpReference));
+ (void) pRefAttr;
+ return mpReference;
+}
+
+SwContentFrame* SwFootnoteFrame::GetRef()
+{
+ const SwContentFrame* pRefAttr = GetRefFromAttr();
+ // check consistency: access to deleted frame?
+ assert(mpReference == pRefAttr || mpReference->IsAnFollow(pRefAttr)
+ || pRefAttr->IsAnFollow(mpReference));
+ (void) pRefAttr;
+ return mpReference;
+}
+#endif
+
+const SwContentFrame* SwFootnoteFrame::GetRefFromAttr() const
+{
+ SwFootnoteFrame* pThis = const_cast<SwFootnoteFrame*>(this);
+ return pThis->GetRefFromAttr();
+}
+
+SwContentFrame* SwFootnoteFrame::GetRefFromAttr()
+{
+ assert(mpAttribute && "invalid Attribute");
+ SwTextNode& rTNd = const_cast<SwTextNode&>(mpAttribute->GetTextNode());
+ SwPosition aPos( rTNd, SwIndex( &rTNd, mpAttribute->GetStart() ));
+ SwContentFrame* pCFrame = rTNd.getLayoutFrame(getRootFrame(), &aPos);
+ return pCFrame;
+}
+
+/** search for last content in the current footnote frame
+
+ OD 2005-12-02 #i27138#
+*/
+SwContentFrame* SwFootnoteFrame::FindLastContent()
+{
+ SwContentFrame* pLastContentFrame( nullptr );
+
+ // find last lower, which is a content frame or contains content.
+ // hidden text frames, empty sections and empty tables have to be skipped.
+ SwFrame* pLastLowerOfFootnote( GetLower() );
+ SwFrame* pTmpLastLower( pLastLowerOfFootnote );
+ while ( pTmpLastLower && pTmpLastLower->GetNext() )
+ {
+ pTmpLastLower = pTmpLastLower->GetNext();
+ if ( ( pTmpLastLower->IsTextFrame() &&
+ !static_cast<SwTextFrame*>(pTmpLastLower)->IsHiddenNow() ) ||
+ ( pTmpLastLower->IsSctFrame() &&
+ static_cast<SwSectionFrame*>(pTmpLastLower)->GetSection() &&
+ static_cast<SwSectionFrame*>(pTmpLastLower)->ContainsContent() ) ||
+ ( pTmpLastLower->IsTabFrame() &&
+ static_cast<SwTabFrame*>(pTmpLastLower)->ContainsContent() ) )
+ {
+ pLastLowerOfFootnote = pTmpLastLower;
+ }
+ }
+
+ // determine last content frame depending on type of found last lower.
+ if ( pLastLowerOfFootnote && pLastLowerOfFootnote->IsTabFrame() )
+ {
+ pLastContentFrame = static_cast<SwTabFrame*>(pLastLowerOfFootnote)->FindLastContent();
+ }
+ else if ( pLastLowerOfFootnote && pLastLowerOfFootnote->IsSctFrame() )
+ {
+ pLastContentFrame = static_cast<SwSectionFrame*>(pLastLowerOfFootnote)->FindLastContent();
+ }
+ else
+ {
+ pLastContentFrame = dynamic_cast<SwContentFrame*>(pLastLowerOfFootnote);
+ }
+
+ return pLastContentFrame;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/hffrm.cxx b/sw/source/core/layout/hffrm.cxx
new file mode 100644
index 000000000..a8e9bfa5a
--- /dev/null
+++ b/sw/source/core/layout/hffrm.cxx
@@ -0,0 +1,768 @@
+/* -*- 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 <pagefrm.hxx>
+#include <fmtcntnt.hxx>
+#include <fmthdft.hxx>
+#include <fmtfsize.hxx>
+#include <viewopt.hxx>
+#include <hffrm.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <sectfrm.hxx>
+#include <flyfrm.hxx>
+#include <frmtool.hxx>
+#include <hfspacingitem.hxx>
+#include <sortedobjs.hxx>
+#include <objectformatter.hxx>
+#include <ndindex.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+static SwTwips lcl_GetFrameMinHeight(const SwLayoutFrame & rFrame)
+{
+ const SwFormatFrameSize &rSz = rFrame.GetFormat()->GetFrameSize();
+ SwTwips nMinHeight;
+
+ switch (rSz.GetHeightSizeType())
+ {
+ case SwFrameSize::Minimum:
+ nMinHeight = rSz.GetHeight();
+
+ break;
+
+ default:
+ nMinHeight = 0;
+ }
+
+ return nMinHeight;
+}
+
+static SwTwips lcl_CalcContentHeight(SwLayoutFrame & frm)
+{
+ SwTwips nRemaining = 0;
+ SwFrame* pFrame = frm.Lower();
+
+ while ( pFrame )
+ {
+ SwTwips nTmp;
+
+ nTmp = pFrame->getFrameArea().Height();
+ nRemaining += nTmp;
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ {
+ nTmp = static_cast<SwTextFrame*>(pFrame)->GetParHeight()
+ - pFrame->getFramePrintArea().Height();
+ // This TextFrame would like to be a bit bigger
+ nRemaining += nTmp;
+ }
+ else if( pFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
+ {
+ nTmp = static_cast<SwSectionFrame*>(pFrame)->Undersize();
+ nRemaining += nTmp;
+ }
+ pFrame = pFrame->GetNext();
+ }
+
+ return nRemaining;
+}
+
+static void lcl_LayoutFrameEnsureMinHeight(SwLayoutFrame & rFrame)
+{
+ SwTwips nMinHeight = lcl_GetFrameMinHeight(rFrame);
+
+ if (rFrame.getFrameArea().Height() < nMinHeight)
+ {
+ rFrame.Grow(nMinHeight - rFrame.getFrameArea().Height());
+ }
+}
+
+SwHeadFootFrame::SwHeadFootFrame( SwFrameFormat * pFormat, SwFrame* pSib, SwFrameType nTypeIn)
+ : SwLayoutFrame( pFormat, pSib )
+{
+ mnFrameType = nTypeIn;
+ SetDerivedVert( false );
+
+ const SwFormatContent &rCnt = pFormat->GetContent();
+
+ OSL_ENSURE( rCnt.GetContentIdx(), "No content for Header." );
+
+ // Have the objects created right now for header and footer
+ bool bOld = bObjsDirect;
+ bObjsDirect = true;
+ SwNodeOffset nIndex = rCnt.GetContentIdx()->GetIndex();
+ ::InsertCnt_( this, pFormat->GetDoc(), ++nIndex );
+ bObjsDirect = bOld;
+}
+
+void SwHeadFootFrame::FormatPrt(SwTwips & nUL, const SwBorderAttrs * pAttrs)
+{
+ if (GetEatSpacing())
+ {
+ /* The minimal height of the print area is the minimal height of the
+ frame without the height needed for borders and shadow. */
+ SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
+
+ nMinHeight -= pAttrs->CalcTop();
+ nMinHeight -= pAttrs->CalcBottom();
+
+ /* If the minimal height of the print area is negative, try to
+ compensate by overlapping */
+ SwTwips nOverlap = 0;
+ if (nMinHeight < 0)
+ {
+ nOverlap = -nMinHeight;
+ nMinHeight = 0;
+ }
+
+ /* Calculate desired height of content. The minimal height has to be
+ adhered. */
+ SwTwips nHeight;
+
+ if ( ! HasFixSize() )
+ nHeight = lcl_CalcContentHeight(*this);
+ else
+ nHeight = nMinHeight;
+
+ if (nHeight < nMinHeight)
+ nHeight = nMinHeight;
+
+ /* calculate initial spacing/line space */
+ SwTwips nSpace, nLine;
+
+ if (IsHeaderFrame())
+ {
+ nSpace = pAttrs->CalcBottom();
+ nLine = pAttrs->CalcBottomLine();
+ }
+ else
+ {
+ nSpace = pAttrs->CalcTop();
+ nLine = pAttrs->CalcTopLine();
+ }
+
+ /* calculate overlap and correct spacing */
+ nOverlap += nHeight - nMinHeight;
+ if (nOverlap < nSpace - nLine)
+ nSpace -= nOverlap;
+ else
+ nSpace = nLine;
+
+ /* calculate real vertical space between frame and print area */
+ if (IsHeaderFrame())
+ nUL = pAttrs->CalcTop() + nSpace;
+ else
+ nUL = pAttrs->CalcBottom() + nSpace;
+
+ /* set print area */
+ SwTwips nLR = pAttrs->CalcLeft( this ) + pAttrs->CalcRight( this );
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+
+ aPrt.Left(pAttrs->CalcLeft(this));
+
+ if (IsHeaderFrame())
+ {
+ aPrt.Top(pAttrs->CalcTop());
+ }
+ else
+ {
+ aPrt.Top(nSpace);
+ }
+
+ aPrt.Width(getFrameArea().Width() - nLR);
+
+ SwTwips nNewHeight;
+
+ if (nUL < getFrameArea().Height())
+ {
+ nNewHeight = getFrameArea().Height() - nUL;
+ }
+ else
+ {
+ nNewHeight = 0;
+ }
+
+ aPrt.Height(nNewHeight);
+ }
+ else
+ {
+ // Set position
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Left( pAttrs->CalcLeft( this ) );
+ aPrt.Top ( pAttrs->CalcTop() );
+
+ // Set sizes - the sizes are given by the surrounding Frame, just
+ // subtract the borders.
+ SwTwips nLR = pAttrs->CalcLeft( this ) + pAttrs->CalcRight( this );
+ aPrt.Width ( getFrameArea().Width() - nLR );
+ aPrt.Height( getFrameArea().Height()- nUL );
+ }
+
+ setFramePrintAreaValid(true);
+}
+
+void SwHeadFootFrame::FormatSize(SwTwips nUL, const SwBorderAttrs * pAttrs)
+{
+ if ( !HasFixSize() )
+ {
+ if( !IsColLocked() )
+ {
+ setFramePrintAreaValid(true);
+ setFrameAreaSizeValid(true);
+
+ const SwTwips nBorder = nUL;
+ SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
+ nMinHeight -= pAttrs->CalcTop();
+ nMinHeight -= pAttrs->CalcBottom();
+
+ if (nMinHeight < 0)
+ nMinHeight = 0;
+
+ ColLock();
+
+ SwTwips nMaxHeight = LONG_MAX;
+ SwTwips nRemaining, nOldHeight;
+ // #i64301#
+ // use the position of the footer printing area to control invalidation
+ // of the first footer content.
+ Point aOldFooterPrtPos;
+
+ do
+ {
+ nOldHeight = getFramePrintArea().Height();
+ SwFrame* pFrame = Lower();
+ // #i64301#
+ if ( pFrame &&
+ aOldFooterPrtPos != ( getFrameArea().Pos() + getFramePrintArea().Pos() ) )
+ {
+ pFrame->InvalidatePos_();
+ aOldFooterPrtPos = getFrameArea().Pos() + getFramePrintArea().Pos();
+ }
+ int nLoopControl = 0;
+ while( pFrame )
+ {
+ pFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i43771# - format also object anchored
+ // at the frame
+ // #i46941# - frame has to be valid.
+ // Note: frame could be invalid after calling its format,
+ // if it's locked
+ OSL_ENSURE( StackHack::IsLocked() || !pFrame->IsTextFrame() ||
+ pFrame->isFrameAreaDefinitionValid() ||
+ static_cast<SwTextFrame*>(pFrame)->IsJoinLocked(),
+ "<SwHeadFootFrame::FormatSize(..)> - text frame invalid and not locked." );
+
+ if ( pFrame->IsTextFrame() && pFrame->isFrameAreaDefinitionValid() )
+ {
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pFrame,
+ *(pFrame->FindPageFrame()) ) )
+ {
+ if (nLoopControl++ < 20)
+ {
+ // restart format with first content
+ pFrame = Lower();
+ continue;
+ }
+ else
+ SAL_WARN("sw", "SwHeadFootFrame::FormatSize: loop detection triggered");
+ }
+ }
+ pFrame = pFrame->GetNext();
+ }
+ nRemaining = 0;
+ pFrame = Lower();
+
+ while ( pFrame )
+ {
+ nRemaining += pFrame->getFrameArea().Height();
+
+ if( pFrame->IsTextFrame() &&
+ static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ // This TextFrame would like to be a bit bigger
+ nRemaining += static_cast<SwTextFrame*>(pFrame)->GetParHeight()
+ - pFrame->getFramePrintArea().Height();
+ else if( pFrame->IsSctFrame() &&
+ static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
+ nRemaining += static_cast<SwSectionFrame*>(pFrame)->Undersize();
+ pFrame = pFrame->GetNext();
+ }
+ if ( nRemaining < nMinHeight )
+ nRemaining = nMinHeight;
+
+ SwTwips nDiff = nRemaining - nOldHeight;
+
+ if( !nDiff )
+ break;
+ if( nDiff < 0 )
+ {
+ nMaxHeight = nOldHeight;
+
+ if( nRemaining <= nMinHeight )
+ nRemaining = ( nMaxHeight + nMinHeight + 1 ) / 2;
+ }
+ else
+ {
+ if (nOldHeight > nMinHeight)
+ nMinHeight = nOldHeight;
+
+ if( nRemaining >= nMaxHeight )
+ nRemaining = ( nMaxHeight + nMinHeight + 1 ) / 2;
+ }
+
+ nDiff = nRemaining - nOldHeight;
+
+ if ( nDiff )
+ {
+ ColUnlock();
+ if ( nDiff > 0 )
+ {
+ if ( Grow( nDiff ) )
+ {
+ pFrame = Lower();
+
+ while ( pFrame )
+ {
+ if( pFrame->IsTextFrame())
+ {
+ SwTextFrame * pTmpFrame = static_cast<SwTextFrame*>(pFrame);
+ if (pTmpFrame->IsUndersized() )
+ {
+ pTmpFrame->InvalidateSize();
+ pTmpFrame->Prepare(PrepareHint::AdjustSizeWithoutFormatting);
+ }
+ }
+ /* #i3568# Undersized sections need to be
+ invalidated too. */
+ else if (pFrame->IsSctFrame())
+ {
+ SwSectionFrame * pTmpFrame =
+ static_cast<SwSectionFrame*>(pFrame);
+ if (pTmpFrame->IsUndersized() )
+ {
+ pTmpFrame->InvalidateSize();
+ pTmpFrame->Prepare(PrepareHint::AdjustSizeWithoutFormatting);
+ }
+ }
+ pFrame = pFrame->GetNext();
+ }
+ }
+ }
+ else
+ Shrink( -nDiff );
+ // Quickly update the position
+
+ MakePos();
+ ColLock();
+ }
+ else
+ break;
+ // Don't overwrite the lower edge of the upper
+ if ( GetUpper() && getFrameArea().Height() )
+ {
+ const SwTwips nDeadLine = GetUpper()->getFrameArea().Top() + GetUpper()->getFramePrintArea().Bottom();
+ const SwTwips nBot = getFrameArea().Bottom();
+
+ if ( nBot > nDeadLine )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Bottom( nDeadLine );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( getFrameArea().Height() - nBorder );
+ }
+ }
+
+ setFramePrintAreaValid(true);
+ setFrameAreaSizeValid(true);
+ } while( nRemaining<=nMaxHeight && nOldHeight!=getFramePrintArea().Height() );
+ ColUnlock();
+ }
+
+ setFramePrintAreaValid(true);
+ setFrameAreaSizeValid(true);
+ }
+ else //if (GetType() & FRM_HEADFOOT)
+ {
+ do
+ {
+ if ( getFrameArea().Height() != pAttrs->GetSize().Height() )
+ {
+ ChgSize( Size( getFrameArea().Width(), pAttrs->GetSize().Height()));
+ }
+
+ setFrameAreaSizeValid(true);
+ MakePos();
+ } while ( !isFrameAreaSizeValid() );
+ }
+}
+
+void SwHeadFootFrame::Format(vcl::RenderContext* pRenderContext, const SwBorderAttrs * pAttrs)
+{
+ OSL_ENSURE( pAttrs, "SwFooterFrame::Format, pAttrs is 0." );
+
+ if ( isFramePrintAreaValid() && isFrameAreaSizeValid() )
+ return;
+
+ if ( ! GetEatSpacing() && IsHeaderFrame())
+ {
+ SwLayoutFrame::Format(pRenderContext, pAttrs);
+ }
+ else
+ {
+ lcl_LayoutFrameEnsureMinHeight(*this);
+
+ SwTwips nUL = pAttrs->CalcTop() + pAttrs->CalcBottom();
+
+ if ( !isFramePrintAreaValid() )
+ FormatPrt(nUL, pAttrs);
+
+ if ( !isFrameAreaSizeValid() )
+ FormatSize(nUL, pAttrs);
+ }
+}
+
+SwTwips SwHeadFootFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwTwips nResult;
+
+ if ( IsColLocked() )
+ {
+ nResult = 0;
+ }
+ else if (!GetEatSpacing())
+ {
+ nResult = SwLayoutFrame::GrowFrame(nDist, bTst, bInfo);
+ }
+ else
+ {
+ nResult = 0;
+
+ std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
+ OSL_ENSURE(oAccess, "no border attributes");
+
+ SwBorderAttrs * pAttrs = oAccess->Get();
+
+ /* First assume the whole amount to grow can be provided by eating
+ spacing. */
+ SwTwips nEat = nDist;
+ SwTwips nMaxEat;
+
+ /* calculate maximum eatable spacing */
+ if (IsHeaderFrame())
+ nMaxEat = getFrameArea().Height() - getFramePrintArea().Top() - getFramePrintArea().Height() - pAttrs->CalcBottomLine();
+ else
+ nMaxEat = getFramePrintArea().Top() - pAttrs->CalcTopLine();
+
+ if (nMaxEat < 0)
+ nMaxEat = 0;
+
+ /* If the frame is too small, eat less spacing thus letting the frame
+ grow more. */
+ SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
+ SwTwips nFrameTooSmall = nMinHeight - getFrameArea().Height();
+
+ if (nFrameTooSmall > 0)
+ nEat -= nFrameTooSmall;
+
+ /* No negative eating, not eating more than allowed. */
+ if (nEat < 0)
+ nEat = 0;
+ else if (nEat > nMaxEat)
+ nEat = nMaxEat;
+
+ // Notify fly frame, if header frame
+ // grows. Consider, that 'normal' grow of layout frame already notifies
+ // the fly frames.
+ bool bNotifyFlys = false;
+ if (nEat > 0)
+ {
+ if ( ! bTst)
+ {
+ if (! IsHeaderFrame())
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Top(aPrt.Top() - nEat);
+ aPrt.Height(aPrt.Height() - nEat);
+ }
+
+ InvalidateAll();
+ }
+
+ nResult += nEat;
+ // trigger fly frame notify.
+ if ( IsHeaderFrame() )
+ {
+ bNotifyFlys = true;
+ }
+ }
+
+ if (nDist - nEat > 0)
+ {
+ const SwTwips nFrameGrow =
+ SwLayoutFrame::GrowFrame( nDist - nEat, bTst, bInfo );
+
+ nResult += nFrameGrow;
+ if ( nFrameGrow > 0 )
+ {
+ bNotifyFlys = false;
+ }
+ }
+
+ // notify fly frames, if necessary and triggered.
+ if ( ( nResult > 0 ) && bNotifyFlys )
+ {
+ NotifyLowerObjs();
+ }
+ }
+
+ if ( nResult && !bTst )
+ SetCompletePaint();
+
+ return nResult;
+}
+
+SwTwips SwHeadFootFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwTwips nResult;
+
+ if ( IsColLocked() )
+ {
+ nResult = 0;
+ }
+ else if (! GetEatSpacing())
+ {
+ nResult = SwLayoutFrame::ShrinkFrame(nDist, bTst, bInfo);
+ }
+ else
+ {
+ nResult = 0;
+
+ SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
+ SwTwips nOldHeight = getFrameArea().Height();
+ SwTwips nRest = 0; // Amount to shrink by spitting out spacing
+
+ if ( nOldHeight >= nMinHeight )
+ {
+ /* If the frame's height is bigger than its minimum height, shrink
+ the frame towards its minimum height. If this is not sufficient
+ to provide the shrinking requested provide the rest by spitting
+ out spacing. */
+
+ SwTwips nBiggerThanMin = nOldHeight - nMinHeight;
+
+ if (nBiggerThanMin < nDist)
+ {
+ nRest = nDist - nBiggerThanMin;
+ }
+ /* info: declaration of nRest -> else nRest = 0 */
+ }
+ else
+ /* The frame cannot shrink. Provide shrinking by spitting out
+ spacing. */
+ nRest = nDist;
+
+ // Notify fly frame, if header/footer frame shrinks.
+ // Consider, that 'normal' shrink of layout frame already notifies the fly frames.
+ bool bNotifyFlys = false;
+ if (nRest > 0)
+ {
+ std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
+ OSL_ENSURE(oAccess, "no border attributes");
+
+ SwBorderAttrs * pAttrs = oAccess->Get();
+
+ /* minimal height of print area */
+ SwTwips nMinPrtHeight = nMinHeight
+ - pAttrs->CalcTop()
+ - pAttrs->CalcBottom();
+
+ if (nMinPrtHeight < 0)
+ nMinPrtHeight = 0;
+
+ /* assume all shrinking can be provided */
+ SwTwips nShrink = nRest;
+
+ /* calculate maximum shrinking */
+ SwTwips nMaxShrink = getFramePrintArea().Height() - nMinPrtHeight;
+
+ /* shrink no more than maximum shrinking */
+ if (nShrink > nMaxShrink)
+ {
+ //nRest -= nShrink - nMaxShrink;
+ nShrink = nMaxShrink;
+ }
+
+ if (!bTst)
+ {
+ if (! IsHeaderFrame() )
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Top(aPrt.Top() + nShrink);
+ aPrt.Height(aPrt.Height() - nShrink);
+ }
+
+ InvalidateAll();
+ }
+ nResult += nShrink;
+ // Trigger fly frame notify.
+ if ( IsHeaderFrame() )
+ {
+ bNotifyFlys = true;
+ }
+ }
+
+ /* The shrinking not providable by spitting out spacing has to be done
+ by the frame. */
+ if (nDist - nRest > 0)
+ {
+ SwTwips nShrinkAmount = SwLayoutFrame::ShrinkFrame( nDist - nRest, bTst, bInfo );
+ nResult += nShrinkAmount;
+ if ( nShrinkAmount > 0 )
+ {
+ bNotifyFlys = false;
+ }
+ }
+
+ // Notify fly frames, if necessary.
+ if ( ( nResult > 0 ) && bNotifyFlys )
+ {
+ NotifyLowerObjs();
+ }
+ }
+
+ return nResult;
+}
+
+bool SwHeadFootFrame::GetEatSpacing() const
+{
+ const SwFrameFormat * pFormat = GetFormat();
+ OSL_ENSURE(pFormat, "SwHeadFootFrame: no format?");
+
+ return pFormat->GetHeaderAndFooterEatSpacing().GetValue();
+}
+
+static void DelFlys( const SwLayoutFrame& rFrame, SwPageFrame &rPage)
+{
+ size_t i = 0;
+ while ( rPage.GetSortedObjs() &&
+ rPage.GetSortedObjs()->size() &&
+ i < rPage.GetSortedObjs()->size() )
+ {
+ SwAnchoredObject* pObj = (*rPage.GetSortedObjs())[i];
+ if (SwFlyFrame* pFlyFrame = pObj->DynCastFlyFrame())
+ {
+ if (rFrame.IsAnLower(pFlyFrame))
+ {
+ SwFrame::DestroyFrame(pFlyFrame);
+ // Do not increment index, in this case
+ continue;
+ }
+ }
+ ++i;
+ }
+}
+
+/// Creates or removes headers
+void SwPageFrame::PrepareHeader()
+{
+ SwLayoutFrame *pLay = static_cast<SwLayoutFrame*>(Lower());
+ if ( !pLay )
+ return;
+
+ const SwFormatHeader &rH = static_cast<SwFrameFormat*>(GetDep())->GetHeader();
+
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bOn = !(pSh && (pSh->GetViewOptions()->getBrowseMode() ||
+ pSh->GetViewOptions()->IsWhitespaceHidden()));
+
+ if ( bOn && rH.IsActive() )
+ { //Implant header, but remove first, if already present
+ OSL_ENSURE( rH.GetHeaderFormat(), "FrameFormat for Header not found." );
+
+ if ( pLay->GetFormat() == rH.GetHeaderFormat() )
+ return; // Header is already the correct one.
+
+ if ( pLay->IsHeaderFrame() )
+ { SwLayoutFrame *pDel = pLay;
+ pLay = static_cast<SwLayoutFrame*>(pLay->GetNext());
+ ::DelFlys(*pDel, *this);
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ }
+ OSL_ENSURE( pLay, "Where to with the Header?" );
+ SwHeaderFrame *pH = new SwHeaderFrame( const_cast<SwFrameFormat*>(rH.GetHeaderFormat()), this );
+ pH->Paste( this, pLay );
+ if ( GetUpper() )
+ ::RegistFlys( this, pH );
+ }
+ else if (pLay->IsHeaderFrame())
+ { // Remove header if present.
+ ::DelFlys(*pLay, *this);
+ pLay->Cut();
+ SwFrame::DestroyFrame(pLay);
+ }
+}
+
+/// Creates or removes footer
+void SwPageFrame::PrepareFooter()
+{
+ SwLayoutFrame *pLay = static_cast<SwLayoutFrame*>(Lower());
+ if ( !pLay )
+ return;
+
+ const SwFormatFooter &rF = static_cast<SwFrameFormat*>(GetDep())->GetFooter();
+ while ( pLay->GetNext() )
+ pLay = static_cast<SwLayoutFrame*>(pLay->GetNext());
+
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bOn = !(pSh && (pSh->GetViewOptions()->getBrowseMode() ||
+ pSh->GetViewOptions()->IsWhitespaceHidden()));
+
+ if ( bOn && rF.IsActive() )
+ { //Implant footer, but remove first, if already present
+ OSL_ENSURE( rF.GetFooterFormat(), "FrameFormat for Footer not found." );
+
+ if ( pLay->GetFormat() == rF.GetFooterFormat() )
+ return; // Footer is already the correct one.
+
+ if ( pLay->IsFooterFrame() )
+ {
+ ::DelFlys(*pLay, *this);
+ pLay->Cut();
+ SwFrame::DestroyFrame(pLay);
+ }
+ SwFooterFrame *pF = new SwFooterFrame( const_cast<SwFrameFormat*>(rF.GetFooterFormat()), this );
+ pF->Paste( this );
+ if ( GetUpper() )
+ ::RegistFlys( this, pF );
+ }
+ else if ( pLay->IsFooterFrame() )
+ {
+ // Remove footer if already present
+ ::DelFlys(*pLay, *this);
+ SwViewShell *pShell;
+ if ( pLay->GetPrev() && nullptr != (pShell = getRootFrame()->GetCurrShell()) &&
+ pShell->VisArea().HasArea() )
+ pShell->InvalidateWindows( pShell->VisArea() );
+ pLay->Cut();
+ SwFrame::DestroyFrame(pLay);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx
new file mode 100644
index 000000000..6cdfcfce0
--- /dev/null
+++ b/sw/source/core/layout/layact.cxx
@@ -0,0 +1,2419 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_feature_desktop.h>
+#include <config_wasm_strip.h>
+
+#include <ctime>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewimp.hxx>
+#include <crsrsh.hxx>
+#include <dflyobj.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <viewopt.hxx>
+#include <dbg_lay.hxx>
+#include <layouter.hxx>
+#include <docstat.hxx>
+#include <swevent.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentLayoutAccess.hxx>
+
+#include <sfx2/event.hxx>
+
+#include <ftnidx.hxx>
+#include <vcl/svapp.hxx>
+#include <editeng/opaqitem.hxx>
+#include <SwSmartTagMgr.hxx>
+#include <sal/log.hxx>
+
+#include <layact.hxx>
+#include <swwait.hxx>
+#include <fmtsrnd.hxx>
+#include <docsh.hxx>
+
+#include <anchoreddrawobject.hxx>
+#include <ndtxt.hxx>
+#include <tabfrm.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <flyfrms.hxx>
+#include <mdiexp.hxx>
+#include <sectfrm.hxx>
+#include <acmplwrd.hxx>
+#include <deletelistener.hxx>
+#include <sortedobjs.hxx>
+#include <objectformatter.hxx>
+#include <fntcache.hxx>
+#include <fmtanchr.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <vector>
+#include <tools/diagnose_ex.h>
+
+void SwLayAction::CheckWaitCursor()
+{
+ if (IsReschedule())
+ {
+ ::RescheduleProgress(m_pImp->GetShell()->GetDoc()->GetDocShell());
+ }
+ if ( !m_pWait && IsWaitAllowed() && IsPaint() &&
+ ((std::clock() - m_nStartTicks) * 1000 / CLOCKS_PER_SEC >= CLOCKS_PER_SEC/2) )
+ {
+ m_pWait.reset( new SwWait( *m_pRoot->GetFormat()->GetDoc()->GetDocShell(), true ) );
+ }
+}
+
+// Time over already?
+inline void SwLayAction::CheckIdleEnd()
+{
+ if (!IsInterrupt())
+ m_bInterrupt = bool(GetInputType()) && Application::AnyInput(GetInputType());
+}
+
+void SwLayAction::SetStatBar( bool bNew )
+{
+ if ( bNew )
+ {
+ m_nEndPage = m_pRoot->GetPageNum();
+ m_nEndPage += m_nEndPage * 10 / 100;
+ }
+ else
+ m_nEndPage = USHRT_MAX;
+}
+
+bool SwLayAction::PaintWithoutFlys( const SwRect &rRect, const SwContentFrame *pCnt,
+ const SwPageFrame *pPage )
+{
+ SwRegionRects aTmp( rRect );
+ const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ const SwFlyFrame *pSelfFly = pCnt->FindFlyFrame();
+
+ for ( size_t i = 0; i < rObjs.size() && !aTmp.empty(); ++i )
+ {
+ SwVirtFlyDrawObj *pVirtFly = dynamic_cast<SwVirtFlyDrawObj*>(rObjs[i]->DrawObj());
+ if ( !pVirtFly )
+ continue;
+
+ // do not consider invisible objects
+ const IDocumentDrawModelAccess& rIDDMA = pPage->GetFormat()->getIDocumentDrawModelAccess();
+ if ( !rIDDMA.IsVisibleLayerId( pVirtFly->GetLayer() ) )
+ {
+ continue;
+ }
+
+ SwFlyFrame *pFly = pVirtFly->GetFlyFrame();
+
+ if ( pFly == pSelfFly || !rRect.Overlaps( pFly->getFrameArea() ) )
+ continue;
+
+ if ( pSelfFly && pSelfFly->IsLowerOf( pFly ) )
+ continue;
+
+ if ( pFly->GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() )
+ continue;
+
+ if ( pSelfFly )
+ {
+ const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
+ if ( pVirtFly->GetLayer() == pTmp->GetLayer() )
+ {
+ if ( pVirtFly->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
+ // Only look at things above us, if inside the same layer
+ continue;
+ }
+ else
+ {
+ const bool bLowerOfSelf = pFly->IsLowerOf( pSelfFly );
+ if ( !bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue() )
+ // Things from other layers are only interesting to us if
+ // they're not transparent or lie inwards
+ continue;
+ }
+ }
+
+ // Fly frame without a lower have to be subtracted from paint region.
+ // For checking, if fly frame contains transparent graphic or
+ // has surrounded contour, assure that fly frame has a lower
+ if ( pFly->Lower() &&
+ pFly->Lower()->IsNoTextFrame() &&
+ ( static_cast<SwNoTextFrame*>(pFly->Lower())->IsTransparent() ||
+ pFly->GetFormat()->GetSurround().IsContour() )
+ )
+ {
+ continue;
+ }
+
+ // vcl::Region of a fly frame with transparent background or a transparent
+ // shadow have not to be subtracted from paint region
+ if ( pFly->IsBackgroundTransparent() )
+ {
+ continue;
+ }
+
+ aTmp -= pFly->getFrameArea();
+ }
+
+ bool bRetPaint = false;
+ for ( const auto& rRegionRect : aTmp )
+ bRetPaint |= m_pImp->GetShell()->AddPaintRect( rRegionRect );
+ return bRetPaint;
+}
+
+inline bool SwLayAction::PaintContent_( const SwContentFrame *pContent,
+ const SwPageFrame *pPage,
+ const SwRect &rRect )
+{
+ if ( rRect.HasArea() )
+ {
+ if ( pPage->GetSortedObjs() )
+ return PaintWithoutFlys( rRect, pContent, pPage );
+ else
+ return m_pImp->GetShell()->AddPaintRect( rRect );
+ }
+ return false;
+}
+
+/**
+ * Depending of the type, the Content is output according to its changes, or the area
+ * to be outputted is registered with the region, respectively.
+ */
+void SwLayAction::PaintContent( const SwContentFrame *pCnt,
+ const SwPageFrame *pPage,
+ const SwRect &rOldRect,
+ tools::Long nOldBottom )
+{
+ SwRectFnSet aRectFnSet(pCnt);
+
+ if ( pCnt->IsCompletePaint() || !pCnt->IsTextFrame() )
+ {
+ SwRect aPaint( pCnt->GetPaintArea() );
+ if ( !PaintContent_( pCnt, pPage, aPaint ) )
+ pCnt->ResetCompletePaint();
+ }
+ else
+ {
+ // paint the area between printing bottom and frame bottom and
+ // the area left and right beside the frame, if its height changed.
+ tools::Long nOldHeight = aRectFnSet.GetHeight(rOldRect);
+ tools::Long nNewHeight = aRectFnSet.GetHeight(pCnt->getFrameArea());
+ const bool bHeightDiff = nOldHeight != nNewHeight;
+ if( bHeightDiff )
+ {
+ // consider whole potential paint area.
+ SwRect aDrawRect( pCnt->GetPaintArea() );
+ if( nOldHeight > nNewHeight )
+ nOldBottom = aRectFnSet.GetPrtBottom(*pCnt);
+ aRectFnSet.SetTop( aDrawRect, nOldBottom );
+ PaintContent_( pCnt, pPage, aDrawRect );
+ }
+ // paint content area
+ SwRect aPaintRect = static_cast<SwTextFrame*>(const_cast<SwContentFrame*>(pCnt))->GetPaintSwRect();
+ PaintContent_( pCnt, pPage, aPaintRect );
+ }
+
+ if ( !pCnt->IsRetouche() || pCnt->GetNext() )
+ return;
+
+ const SwFrame *pTmp = pCnt;
+ if( pCnt->IsInSct() )
+ {
+ const SwSectionFrame* pSct = pCnt->FindSctFrame();
+ if( pSct->IsRetouche() && !pSct->GetNext() )
+ pTmp = pSct;
+ }
+ SwRect aRect( pTmp->GetUpper()->GetPaintArea() );
+ aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTmp) );
+ if ( !PaintContent_( pCnt, pPage, aRect ) )
+ pCnt->ResetRetouche();
+}
+
+SwLayAction::SwLayAction( SwRootFrame *pRt, SwViewShellImp *pI ) :
+ m_pRoot( pRt ),
+ m_pImp( pI ),
+ m_pOptTab( nullptr ),
+ m_nPreInvaPage( USHRT_MAX ),
+ m_nStartTicks( std::clock() ),
+ m_nInputType( VclInputFlags::NONE ),
+ m_nEndPage( USHRT_MAX ),
+ m_nCheckPageNum( USHRT_MAX )
+{
+ m_bPaintExtraData = ::IsExtraData( m_pImp->GetShell()->GetDoc() );
+ m_bPaint = m_bComplete = m_bWaitAllowed = m_bCheckPages = true;
+ m_bInterrupt = m_bAgain = m_bNextCycle = m_bCalcLayout = m_bIdle = m_bReschedule =
+ m_bUpdateExpFields = m_bBrowseActionStop = m_bActionInProgress = false;
+ // init new flag <mbFormatContentOnInterrupt>.
+ mbFormatContentOnInterrupt = false;
+
+ assert(!m_pImp->m_pLayAction); // there can be only one SwLayAction
+ m_pImp->m_pLayAction = this; // register there
+}
+
+SwLayAction::~SwLayAction()
+{
+ OSL_ENSURE( !m_pWait, "Wait object not destroyed" );
+ m_pImp->m_pLayAction = nullptr; // unregister
+}
+
+void SwLayAction::Reset()
+{
+ SetAgain(false);
+ m_pOptTab = nullptr;
+ m_nStartTicks = std::clock();
+ m_nInputType = VclInputFlags::NONE;
+ m_nEndPage = m_nPreInvaPage = m_nCheckPageNum = USHRT_MAX;
+ m_bPaint = m_bComplete = m_bWaitAllowed = m_bCheckPages = true;
+ m_bInterrupt = m_bNextCycle = m_bCalcLayout = m_bIdle = m_bReschedule =
+ m_bUpdateExpFields = m_bBrowseActionStop = false;
+}
+
+bool SwLayAction::RemoveEmptyBrowserPages()
+{
+ // switching from the normal to the browser mode, empty pages may be
+ // retained for an annoyingly long time, so delete them here
+ bool bRet = false;
+ const SwViewShell *pSh = m_pRoot->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
+ do
+ {
+ if ( (pPage->GetSortedObjs() && pPage->GetSortedObjs()->size()) ||
+ pPage->ContainsContent() ||
+ pPage->FindFootnoteCont() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ else
+ {
+ bRet = true;
+ SwPageFrame *pDel = pPage;
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ }
+ } while ( pPage );
+ }
+ return bRet;
+}
+
+void SwLayAction::SetAgain(bool bAgain)
+{
+ if (bAgain == m_bAgain)
+ return;
+
+ m_bAgain = bAgain;
+
+ assert(m_aFrameStack.size() == m_aFrameDeleteGuards.size());
+ size_t nCount = m_aFrameStack.size();
+ if (m_bAgain)
+ {
+ // LayAction::FormatLayout is now flagged to exit early and will avoid
+ // dereferencing any SwFrames in the stack of FormatLayouts so allow
+ // their deletion
+ for (size_t i = 0; i < nCount; ++i)
+ m_aFrameDeleteGuards[i].reset();
+ }
+ else
+ {
+ // LayAction::FormatLayout is now continue normally and will
+ // dereference the top SwFrame in the stack of m_aFrameStack as each
+ // FormatLevel returns so disallow their deletion
+ for (size_t i = 0; i < nCount; ++i)
+ m_aFrameDeleteGuards[i] = std::make_unique<SwFrameDeleteGuard>(m_aFrameStack[i]);
+ }
+}
+
+void SwLayAction::PushFormatLayout(SwFrame* pLow)
+{
+ /* Workaround crash seen in crashtesting with fdo53985-1.docx
+
+ Lock pLow against getting deleted when it will be dereferenced
+ after FormatLayout
+
+ If SetAgain is called to make SwLayAction exit early to avoid that
+ dereference, then it clears these guards
+ */
+ m_aFrameStack.push_back(pLow);
+ m_aFrameDeleteGuards.push_back(std::make_unique<SwFrameDeleteGuard>(pLow));
+}
+
+void SwLayAction::PopFormatLayout()
+{
+ m_aFrameDeleteGuards.pop_back();
+ m_aFrameStack.pop_back();
+}
+
+void SwLayAction::Action(OutputDevice* pRenderContext)
+{
+ m_bActionInProgress = true;
+
+ //TurboMode? Hands-off during idle-format
+ if ( IsPaint() && !IsIdle() && TurboAction() )
+ {
+ m_pWait.reset();
+ m_pRoot->ResetTurboFlag();
+ m_bActionInProgress = false;
+ m_pRoot->DeleteEmptySct();
+ return;
+ }
+ else if ( m_pRoot->GetTurbo() )
+ {
+ m_pRoot->DisallowTurbo();
+ const SwFrame *pFrame = m_pRoot->GetTurbo();
+ m_pRoot->ResetTurbo();
+ pFrame->InvalidatePage();
+ }
+ m_pRoot->DisallowTurbo();
+
+ if ( IsCalcLayout() )
+ SetCheckPages( false );
+
+ InternalAction(pRenderContext);
+ if (RemoveEmptyBrowserPages())
+ SetAgain(true);
+ while ( IsAgain() )
+ {
+ SetAgain(false);
+ m_bNextCycle = false;
+ InternalAction(pRenderContext);
+ if (RemoveEmptyBrowserPages())
+ SetAgain(true);
+ }
+ m_pRoot->DeleteEmptySct();
+
+ m_pWait.reset();
+
+ //Turbo-Action permitted again for all cases.
+ m_pRoot->ResetTurboFlag();
+ m_pRoot->ResetTurbo();
+
+ SetCheckPages( true );
+
+ m_bActionInProgress = false;
+}
+
+SwPageFrame* SwLayAction::CheckFirstVisPage( SwPageFrame *pPage )
+{
+ SwContentFrame *pCnt = pPage->FindFirstBodyContent();
+ SwContentFrame *pChk = pCnt;
+ bool bPageChgd = false;
+ while ( pCnt && pCnt->IsFollow() )
+ pCnt = pCnt->FindMaster();
+ if ( pCnt && pChk != pCnt )
+ { bPageChgd = true;
+ pPage = pCnt->FindPageFrame();
+ }
+
+ if ( !pPage->GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
+ {
+ SwFootnoteContFrame *pCont = pPage->FindFootnoteCont();
+ if ( pCont )
+ {
+ pCnt = pCont->ContainsContent();
+ pChk = pCnt;
+ while ( pCnt && pCnt->IsFollow() )
+ pCnt = static_cast<SwContentFrame*>(pCnt->FindPrev());
+ if ( pCnt && pCnt != pChk )
+ {
+ if ( bPageChgd )
+ {
+ // Use the 'topmost' page
+ SwPageFrame *pTmp = pCnt->FindPageFrame();
+ if ( pPage->GetPhyPageNum() > pTmp->GetPhyPageNum() )
+ pPage = pTmp;
+ }
+ else
+ pPage = pCnt->FindPageFrame();
+ }
+ }
+ }
+ return pPage;
+}
+
+// unlock position on start and end of page
+// layout process.
+static void unlockPositionOfObjects( SwPageFrame *pPageFrame )
+{
+ assert( pPageFrame );
+
+ SwSortedObjs* pObjs = pPageFrame->GetSortedObjs();
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pObj : *pObjs)
+ {
+ pObj->UnlockPosition();
+ }
+ }
+}
+
+void SwLayAction::InternalAction(OutputDevice* pRenderContext)
+{
+ OSL_ENSURE( m_pRoot->Lower()->IsPageFrame(), ":-( No page below the root.");
+
+ m_pRoot->Calc(pRenderContext);
+
+ // Figure out the first invalid page or the first one to be formatted,
+ // respectively. A complete-action means the first invalid page.
+ // However, the first page to be formatted might be the one having the
+ // number 1. If we're doing a fake formatting, the number of the first
+ // page is the number of the first visible page.
+ SwPageFrame *pPage = IsComplete() ? static_cast<SwPageFrame*>(m_pRoot->Lower()) :
+ m_pImp->GetFirstVisPage(pRenderContext);
+ if ( !pPage )
+ pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
+
+ // If there's a first-flow-Content in the first visible page that's also a Follow,
+ // we switch the page back to the original master of that Content.
+ if ( !IsComplete() )
+ pPage = CheckFirstVisPage( pPage );
+ sal_uInt16 nFirstPageNum = pPage->GetPhyPageNum();
+
+ while ( pPage && !pPage->IsInvalid() && !pPage->IsInvalidFly() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+
+ IDocumentLayoutAccess& rLayoutAccess = m_pRoot->GetFormat()->getIDocumentLayoutAccess();
+ const bool bNoLoop = pPage && SwLayouter::StartLoopControl(m_pRoot->GetFormat()->GetDoc(), pPage);
+ sal_uInt16 nPercentPageNum = 0;
+
+ auto lcl_isLayoutLooping = [&]()
+ {
+ const bool bAgain = this->IsAgain();
+ if (bAgain && bNoLoop)
+ rLayoutAccess.GetLayouter()->EndLoopControl();
+ return bAgain;
+ };
+
+ while ( (pPage && !IsInterrupt()) || m_nCheckPageNum != USHRT_MAX )
+ {
+ // note: this is the only place that consumes and resets m_nCheckPageNum
+ if ((IsInterrupt() || !pPage) && m_nCheckPageNum != USHRT_MAX)
+ {
+ if (!pPage || m_nCheckPageNum < pPage->GetPhyPageNum())
+ {
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
+ while (pPg && pPg->GetPhyPageNum() < m_nCheckPageNum)
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ if (pPg)
+ pPage = pPg;
+ if (!pPage)
+ break;
+ }
+
+ SwPageFrame *pTmp = pPage->GetPrev() ?
+ static_cast<SwPageFrame*>(pPage->GetPrev()) : pPage;
+ SetCheckPages( true );
+ SwFrame::CheckPageDescs( pPage, true, &pTmp );
+ SetCheckPages( false );
+ m_nCheckPageNum = USHRT_MAX;
+ pPage = pTmp;
+ continue;
+ }
+
+ if ( m_nEndPage != USHRT_MAX && pPage->GetPhyPageNum() > nPercentPageNum )
+ {
+ nPercentPageNum = pPage->GetPhyPageNum();
+ ::SetProgressState( nPercentPageNum, m_pImp->GetShell()->GetDoc()->GetDocShell());
+ }
+ m_pOptTab = nullptr;
+
+ // No Shortcut for Idle or CalcLayout
+ const bool bTakeShortcut = !IsIdle() && !IsComplete() && IsShortCut(pPage);
+
+ m_pRoot->DeleteEmptySct();
+ if (lcl_isLayoutLooping()) return;
+
+ if (!bTakeShortcut)
+ {
+ while ( !IsInterrupt() && !IsNextCycle() &&
+ ((pPage->GetSortedObjs() && pPage->IsInvalidFly()) || pPage->IsInvalid()) )
+ {
+ unlockPositionOfObjects( pPage );
+
+ SwObjectFormatter::FormatObjsAtFrame( *pPage, *pPage, this );
+ if ( !pPage->GetSortedObjs() )
+ {
+ // If there are no (more) Flys, the flags are superfluous.
+ pPage->ValidateFlyLayout();
+ pPage->ValidateFlyContent();
+ }
+ // change condition
+ while ( !IsInterrupt() && !IsNextCycle() &&
+ ( pPage->IsInvalid() ||
+ (pPage->GetSortedObjs() && pPage->IsInvalidFly()) ) )
+ {
+ PROTOCOL( pPage, PROT::FileInit, DbgAction::NONE, nullptr)
+ if (lcl_isLayoutLooping()) return;
+
+ // new loop control
+ int nLoopControlRuns_1 = 0;
+ const int nLoopControlMax = 20;
+
+ while ( !IsNextCycle() && pPage->IsInvalidLayout() )
+ {
+ pPage->ValidateLayout();
+
+ if ( ++nLoopControlRuns_1 > nLoopControlMax )
+ {
+ OSL_FAIL( "LoopControl_1 in SwLayAction::InternalAction" );
+ break;
+ }
+
+ FormatLayout( pRenderContext, pPage );
+ if (lcl_isLayoutLooping()) return;
+ }
+ // change condition
+ if ( !IsNextCycle() &&
+ ( pPage->IsInvalidContent() ||
+ (pPage->GetSortedObjs() && pPage->IsInvalidFly()) ) )
+ {
+ pPage->ValidateFlyInCnt();
+ pPage->ValidateContent();
+ pPage->ValidateFlyLayout();
+ pPage->ValidateFlyContent();
+ if ( !FormatContent( pPage ) )
+ {
+ if (lcl_isLayoutLooping()) return;
+ pPage->InvalidateContent();
+ pPage->InvalidateFlyInCnt();
+ pPage->InvalidateFlyLayout();
+ pPage->InvalidateFlyContent();
+ if ( IsBrowseActionStop() )
+ m_bInterrupt = true;
+ }
+ }
+ if( bNoLoop )
+ rLayoutAccess.GetLayouter()->LoopControl( pPage );
+ }
+
+ unlockPositionOfObjects( pPage );
+ }
+
+ // A previous page may be invalid again.
+ if (lcl_isLayoutLooping()) return;
+ if ( !pPage->GetSortedObjs() )
+ {
+ // If there are no (more) Flys, the flags are superfluous.
+ pPage->ValidateFlyLayout();
+ pPage->ValidateFlyContent();
+ }
+ if ( !IsInterrupt() )
+ {
+ SetNextCycle( false );
+
+ if ( m_nPreInvaPage != USHRT_MAX )
+ {
+ if( !IsComplete() && m_nPreInvaPage + 2 < nFirstPageNum )
+ {
+ m_pImp->SetFirstVisPageInvalid();
+ SwPageFrame *pTmpPage = m_pImp->GetFirstVisPage(pRenderContext);
+ nFirstPageNum = pTmpPage->GetPhyPageNum();
+ if( m_nPreInvaPage < nFirstPageNum )
+ {
+ m_nPreInvaPage = nFirstPageNum;
+ pPage = pTmpPage;
+ }
+ }
+ while ( pPage->GetPrev() && pPage->GetPhyPageNum() > m_nPreInvaPage )
+ pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
+ m_nPreInvaPage = USHRT_MAX;
+ }
+
+ while ( pPage->GetPrev() &&
+ ( static_cast<SwPageFrame*>(pPage->GetPrev())->IsInvalid() ||
+ ( static_cast<SwPageFrame*>(pPage->GetPrev())->GetSortedObjs() &&
+ static_cast<SwPageFrame*>(pPage->GetPrev())->IsInvalidFly())) &&
+ (static_cast<SwPageFrame*>(pPage->GetPrev())->GetPhyPageNum() >=
+ nFirstPageNum) )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
+ }
+
+ // Continue to the next invalid page
+ while ( pPage && !pPage->IsInvalid() &&
+ (!pPage->GetSortedObjs() || !pPage->IsInvalidFly()) )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ if( bNoLoop )
+ rLayoutAccess.GetLayouter()->LoopControl( pPage );
+ }
+ CheckIdleEnd();
+ }
+
+ if ((bTakeShortcut || !pPage) && !IsInterrupt() &&
+ (m_pRoot->IsSuperfluous() || m_pRoot->IsAssertFlyPages()) )
+ {
+ // tdf#139426 allow suppression of AssertFlyPages
+ if ( m_pRoot->IsAssertFlyPages() && !m_pRoot->IsTableUpdateInProgress())
+ {
+ m_pRoot->AssertFlyPages();
+ }
+ if ( m_pRoot->IsSuperfluous() )
+ {
+ bool bOld = IsAgain();
+ m_pRoot->RemoveSuperfluous();
+ SetAgain(bOld);
+ }
+ if (lcl_isLayoutLooping()) return;
+ pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
+ while ( pPage && !pPage->IsInvalid() && !pPage->IsInvalidFly() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ while ( pPage && pPage->GetNext() &&
+ pPage->GetPhyPageNum() < nFirstPageNum )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ else if (bTakeShortcut)
+ break;
+ }
+ if ( IsInterrupt() && pPage )
+ {
+ // If we have input, we don't want to format content anymore, but
+ // we still should clean the layout.
+ // Otherwise, the following situation might arise:
+ // The user enters some text at the end of the paragraph of the last
+ // page, causing the paragraph to create a Follow for the next page.
+ // Meanwhile the user continues typing, so we have input while
+ // still formatting.
+ // The paragraph on the new page has already been partially formatted,
+ // and the new page has been fully formatted and is set to CompletePaint,
+ // but hasn't added itself to the area to be output. Then we paint,
+ // the CompletePaint of the page is reset because the new paragraph
+ // already added itself, but the borders of the page haven't been painted
+ // yet.
+ // Oh well, with the inevitable following LayAction, the page doesn't
+ // register itself, because it's (LayoutFrame) flags have been reset
+ // already - the border of the page will never be painted.
+ SwPageFrame *pPg = pPage;
+ if (lcl_isLayoutLooping()) return;
+ const SwRect &rVis = m_pImp->GetShell()->VisArea();
+
+ while( pPg && pPg->getFrameArea().Bottom() < rVis.Top() )
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ if( pPg != pPage )
+ pPg = pPg ? static_cast<SwPageFrame*>(pPg->GetPrev()) : pPage;
+
+ // set flag for interrupt content formatting
+ mbFormatContentOnInterrupt = true;
+ tools::Long nBottom = rVis.Bottom();
+ // #i42586# - format current page, if idle action is active
+ // This is an optimization for the case that the interrupt is created by
+ // the move of a form control object, which is represented by a window.
+ while ( pPg && ( pPg->getFrameArea().Top() < nBottom ||
+ ( IsIdle() && pPg == pPage ) ) )
+ {
+ unlockPositionOfObjects( pPg );
+
+ if (lcl_isLayoutLooping()) return;
+
+ // new loop control
+ int nLoopControlRuns_2 = 0;
+ const int nLoopControlMax = 20;
+
+ // special case: interrupt content formatting
+ // conditions are incorrect and are too strict.
+ // adjust interrupt formatting to normal page formatting - see above.
+ while ( ( mbFormatContentOnInterrupt &&
+ ( pPg->IsInvalid() ||
+ ( pPg->GetSortedObjs() && pPg->IsInvalidFly() ) ) ) ||
+ ( !mbFormatContentOnInterrupt && pPg->IsInvalidLayout() ) )
+ {
+ if (lcl_isLayoutLooping()) return;
+ // format also at-page anchored objects
+ SwObjectFormatter::FormatObjsAtFrame( *pPg, *pPg, this );
+ if ( !pPg->GetSortedObjs() )
+ {
+ pPg->ValidateFlyLayout();
+ pPg->ValidateFlyContent();
+ }
+
+ // new loop control
+ int nLoopControlRuns_3 = 0;
+
+ while ( pPg->IsInvalidLayout() )
+ {
+ pPg->ValidateLayout();
+
+ if ( ++nLoopControlRuns_3 > nLoopControlMax )
+ {
+ OSL_FAIL( "LoopControl_3 in Interrupt formatting in SwLayAction::InternalAction" );
+ break;
+ }
+
+ FormatLayout( pRenderContext, pPg );
+ if (lcl_isLayoutLooping()) return;
+ }
+
+ if ( mbFormatContentOnInterrupt &&
+ ( pPg->IsInvalidContent() ||
+ ( pPg->GetSortedObjs() && pPg->IsInvalidFly() ) ) )
+ {
+ pPg->ValidateFlyInCnt();
+ pPg->ValidateContent();
+ pPg->ValidateFlyLayout();
+ pPg->ValidateFlyContent();
+
+ if ( ++nLoopControlRuns_2 > nLoopControlMax )
+ {
+ OSL_FAIL( "LoopControl_2 in Interrupt formatting in SwLayAction::InternalAction" );
+ break;
+ }
+
+ if ( !FormatContent( pPg ) )
+ {
+ if (lcl_isLayoutLooping()) return;
+ pPg->InvalidateContent();
+ pPg->InvalidateFlyInCnt();
+ pPg->InvalidateFlyLayout();
+ pPg->InvalidateFlyContent();
+ }
+ // we are satisfied if the content is formatted once complete.
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ unlockPositionOfObjects( pPg );
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ }
+ // reset flag for special interrupt content formatting.
+ mbFormatContentOnInterrupt = false;
+ }
+ m_pOptTab = nullptr;
+ if( bNoLoop )
+ rLayoutAccess.GetLayouter()->EndLoopControl();
+}
+
+bool SwLayAction::TurboAction_( const SwContentFrame *pCnt )
+{
+
+ const SwPageFrame *pPage = nullptr;
+ if ( !pCnt->isFrameAreaDefinitionValid() || pCnt->IsCompletePaint() || pCnt->IsRetouche() )
+ {
+ const SwRect aOldRect( pCnt->UnionFrame( true ) );
+ const tools::Long nOldBottom = pCnt->getFrameArea().Top() + pCnt->getFramePrintArea().Bottom();
+ pCnt->Calc(m_pImp->GetShell()->GetOut());
+ if ( pCnt->getFrameArea().Bottom() < aOldRect.Bottom() )
+ pCnt->SetRetouche();
+
+ pPage = pCnt->FindPageFrame();
+ PaintContent( pCnt, pPage, aOldRect, nOldBottom );
+
+ if ( !pCnt->GetValidLineNumFlag() && pCnt->IsTextFrame() )
+ {
+ const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pCnt)->GetAllLines();
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCnt))->RecalcAllLines();
+ if ( nAllLines != static_cast<const SwTextFrame*>(pCnt)->GetAllLines() )
+ {
+ if ( IsPaintExtraData() )
+ m_pImp->GetShell()->AddPaintRect( pCnt->getFrameArea() );
+ // This is to calculate the remaining LineNums on the page,
+ // and we don't stop processing here. To perform this inside RecalcAllLines
+ // would be expensive, because we would have to notify the page even
+ // in unnecessary cases (normal actions).
+ const SwContentFrame *pNxt = pCnt->GetNextContentFrame();
+ while ( pNxt &&
+ (pNxt->IsInTab() || pNxt->IsInDocBody() != pCnt->IsInDocBody()) )
+ pNxt = pNxt->GetNextContentFrame();
+ if ( pNxt )
+ pNxt->InvalidatePage();
+ }
+ return false;
+ }
+
+ if ( pPage->IsInvalidLayout() || (pPage->GetSortedObjs() && pPage->IsInvalidFly()) )
+ return false;
+ }
+ if ( !pPage )
+ pPage = pCnt->FindPageFrame();
+
+ // format floating screen objects at content frame.
+ if ( pCnt->IsTextFrame() &&
+ !SwObjectFormatter::FormatObjsAtFrame( *const_cast<SwContentFrame*>(pCnt),
+ *pPage, this ) )
+ {
+ return false;
+ }
+
+ if ( pPage->IsInvalidContent() )
+ return false;
+ return true;
+}
+
+bool SwLayAction::TurboAction()
+{
+ bool bRet = true;
+
+ if ( m_pRoot->GetTurbo() )
+ {
+ if ( !TurboAction_( m_pRoot->GetTurbo() ) )
+ {
+ CheckIdleEnd();
+ bRet = false;
+ }
+ m_pRoot->ResetTurbo();
+ }
+ else
+ bRet = false;
+ return bRet;
+}
+
+static bool lcl_IsInvaLay( const SwFrame *pFrame, tools::Long nBottom )
+{
+ return !pFrame->isFrameAreaDefinitionValid() ||
+ (pFrame->IsCompletePaint() && ( pFrame->getFrameArea().Top() < nBottom ) );
+}
+
+static const SwFrame *lcl_FindFirstInvaLay( const SwFrame *pFrame, tools::Long nBottom )
+{
+ OSL_ENSURE( pFrame->IsLayoutFrame(), "FindFirstInvaLay, no LayFrame" );
+
+ if (lcl_IsInvaLay(pFrame, nBottom))
+ return pFrame;
+ pFrame = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
+ while ( pFrame )
+ {
+ if ( pFrame->IsLayoutFrame() )
+ {
+ if (lcl_IsInvaLay(pFrame, nBottom))
+ return pFrame;
+ const SwFrame *pTmp = lcl_FindFirstInvaLay( pFrame, nBottom );
+ if ( nullptr != pTmp )
+ return pTmp;
+ }
+ pFrame = pFrame->GetNext();
+ }
+ return nullptr;
+}
+
+static const SwFrame *lcl_FindFirstInvaContent( const SwLayoutFrame *pLay, tools::Long nBottom,
+ const SwContentFrame *pFirst )
+{
+ const SwContentFrame *pCnt = pFirst ? pFirst->GetNextContentFrame() :
+ pLay->ContainsContent();
+ while ( pCnt )
+ {
+ if ( !pCnt->isFrameAreaDefinitionValid() || pCnt->IsCompletePaint() )
+ {
+ if ( pCnt->getFrameArea().Top() <= nBottom )
+ return pCnt;
+ }
+
+ if ( pCnt->GetDrawObjs() )
+ {
+ const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ if ( static_cast<const SwFlyInContentFrame*>(pFly)->IsInvalid() ||
+ pFly->IsCompletePaint() )
+ {
+ if ( pFly->getFrameArea().Top() <= nBottom )
+ return pFly;
+ }
+ const SwFrame *pFrame = lcl_FindFirstInvaContent( pFly, nBottom, nullptr );
+ if ( pFrame && pFrame->getFrameArea().Bottom() <= nBottom )
+ return pFrame;
+ }
+ }
+ }
+ }
+ if ( pCnt->getFrameArea().Top() > nBottom && !pCnt->IsInTab() )
+ return nullptr;
+ pCnt = pCnt->GetNextContentFrame();
+ if ( !pLay->IsAnLower( pCnt ) )
+ break;
+ }
+ return nullptr;
+}
+
+// consider drawing objects
+static const SwAnchoredObject* lcl_FindFirstInvaObj( const SwPageFrame* _pPage,
+ tools::Long _nBottom )
+{
+ OSL_ENSURE( _pPage->GetSortedObjs(), "FindFirstInvaObj, no Objs" );
+
+ for (SwAnchoredObject* pObj : *_pPage->GetSortedObjs())
+ {
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->getFrameArea().Top() <= _nBottom )
+ {
+ if ( pFly->IsInvalid() || pFly->IsCompletePaint() )
+ return pFly;
+
+ const SwFrame* pTmp;
+ if ( nullptr != (pTmp = lcl_FindFirstInvaContent( pFly, _nBottom, nullptr )) &&
+ pTmp->getFrameArea().Top() <= _nBottom )
+ return pFly;
+ }
+ }
+ else if ( auto pDrawObject = dynamic_cast< const SwAnchoredDrawObject *>( pObj ) )
+ {
+ if ( !pDrawObject->IsValidPos() )
+ {
+ return pObj;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/* Returns True if the page lies directly below or right of the visible area.
+ *
+ * It's possible for things to change in such a way that the processing
+ * (of the caller!) has to continue with the predecessor of the passed page.
+ * The parameter might therefore get modified!
+ * For BrowseMode, you may even activate the ShortCut if the invalid content
+ * of the page lies below the visible area.
+ */
+bool SwLayAction::IsShortCut( SwPageFrame *&prPage )
+{
+ vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
+ bool bRet = false;
+ const SwViewShell *pSh = m_pRoot->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+
+ // If the page is not valid, we quickly format it, otherwise
+ // there's gonna be no end of trouble
+ if ( !prPage->isFrameAreaDefinitionValid() )
+ {
+ if ( bBrowse )
+ {
+ // format complete page
+ // Thus, loop on all lowers of the page <prPage>, instead of only
+ // format its first lower.
+ // NOTE: In online layout (bBrowse == true) a page can contain
+ // a header frame and/or a footer frame beside the body frame.
+ prPage->Calc(pRenderContext);
+ SwFrame* pPageLowerFrame = prPage->Lower();
+ while ( pPageLowerFrame )
+ {
+ pPageLowerFrame->Calc(pRenderContext);
+ pPageLowerFrame = pPageLowerFrame->GetNext();
+ }
+ }
+ else
+ FormatLayout( pSh ? pSh->GetOut() : nullptr, prPage );
+ if ( IsAgain() )
+ return false;
+ }
+
+ const SwRect &rVis = m_pImp->GetShell()->VisArea();
+ if ( (prPage->getFrameArea().Top() >= rVis.Bottom()) ||
+ (prPage->getFrameArea().Left()>= rVis.Right()) )
+ {
+ bRet = true;
+
+ // This is going to be a bit nasty: The first ContentFrame of this
+ // page in the Body text needs formatting; if it changes the page during
+ // that process, I need to start over a page further back, because we
+ // have been processing a PageBreak.
+ // Even more uncomfortable: The next ContentFrame must be formatted,
+ // because it's possible for empty pages to exist temporarily (for example
+ // a paragraph across multiple pages gets deleted or reduced in size).
+
+ // This is irrelevant for the browser, if the last Cnt above it
+ // isn't visible anymore.
+
+ const SwPageFrame *p2ndPage = prPage;
+ const SwContentFrame *pContent;
+ const SwLayoutFrame* pBody = p2ndPage->FindBodyCont();
+ if( p2ndPage->IsFootnotePage() && pBody )
+ pBody = static_cast<const SwLayoutFrame*>(pBody->GetNext());
+ pContent = pBody ? pBody->ContainsContent() : nullptr;
+ while ( p2ndPage && !pContent )
+ {
+ p2ndPage = static_cast<const SwPageFrame*>(p2ndPage->GetNext());
+ if( p2ndPage )
+ {
+ pBody = p2ndPage->FindBodyCont();
+ if( p2ndPage->IsFootnotePage() && pBody )
+ pBody = static_cast<const SwLayoutFrame*>(pBody->GetNext());
+ pContent = pBody ? pBody->ContainsContent() : nullptr;
+ }
+ }
+ if ( pContent )
+ {
+ bool bTstCnt = true;
+ if ( bBrowse )
+ {
+ // Is the Cnt before already invisible?
+ const SwFrame *pLst = pContent;
+ if ( pLst->IsInTab() )
+ pLst = pContent->FindTabFrame();
+ if ( pLst->IsInSct() )
+ pLst = pContent->FindSctFrame();
+ pLst = pLst->FindPrev();
+ if ( pLst &&
+ (pLst->getFrameArea().Top() >= rVis.Bottom() ||
+ pLst->getFrameArea().Left()>= rVis.Right()) )
+ {
+ bTstCnt = false;
+ }
+ }
+
+ if ( bTstCnt )
+ {
+ // check after each frame calculation,
+ // if the content frame has changed the page. If yes, no other
+ // frame calculation is performed
+ bool bPageChg = false;
+
+ if ( pContent->IsInSct() )
+ {
+ const SwSectionFrame *pSct = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindSctFrame();
+ if ( !pSct->isFrameAreaDefinitionValid() )
+ {
+ pSct->Calc(pRenderContext);
+ pSct->SetCompletePaint();
+ if ( IsAgain() )
+ return false;
+
+ bPageChg = pContent->FindPageFrame() != p2ndPage &&
+ prPage->GetPrev();
+ }
+ }
+
+ if ( !bPageChg && !pContent->isFrameAreaDefinitionValid() )
+ {
+ pContent->Calc(pRenderContext);
+ pContent->SetCompletePaint();
+ if ( IsAgain() )
+ return false;
+
+ bPageChg = pContent->FindPageFrame() != p2ndPage &&
+ prPage->GetPrev();
+ }
+
+ if ( !bPageChg && pContent->IsInTab() )
+ {
+ const SwTabFrame *pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindTabFrame();
+ if ( !pTab->isFrameAreaDefinitionValid() )
+ {
+ pTab->Calc(pRenderContext);
+ pTab->SetCompletePaint();
+ if ( IsAgain() )
+ return false;
+
+ bPageChg = pContent->FindPageFrame() != p2ndPage &&
+ prPage->GetPrev();
+ }
+ }
+
+ if ( !bPageChg && pContent->IsInSct() )
+ {
+ const SwSectionFrame *pSct = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindSctFrame();
+ if ( !pSct->isFrameAreaDefinitionValid() )
+ {
+ pSct->Calc(pRenderContext);
+ pSct->SetCompletePaint();
+ if ( IsAgain() )
+ return false;
+
+ bPageChg = pContent->FindPageFrame() != p2ndPage &&
+ prPage->GetPrev();
+ }
+ }
+
+ if ( bPageChg )
+ {
+ bRet = false;
+ const SwPageFrame* pTmp = pContent->FindPageFrame();
+ if ( pTmp->GetPhyPageNum() < prPage->GetPhyPageNum() &&
+ pTmp->IsInvalid() )
+ {
+ prPage = const_cast<SwPageFrame*>(pTmp);
+ }
+ else
+ {
+ prPage = static_cast<SwPageFrame*>(prPage->GetPrev());
+ }
+ }
+ // no shortcut, if at previous page
+ // an anchored object is registered, whose anchor is <pContent>.
+ else if ( prPage->GetPrev() )
+ {
+ SwSortedObjs* pObjs =
+ static_cast<SwPageFrame*>(prPage->GetPrev())->GetSortedObjs();
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pObj : *pObjs)
+ {
+ if ( pObj->GetAnchorFrameContainingAnchPos() == pContent )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( !bRet && bBrowse )
+ {
+ const tools::Long nBottom = rVis.Bottom();
+ const SwAnchoredObject* pObj( nullptr );
+ if ( prPage->GetSortedObjs() &&
+ (prPage->IsInvalidFlyLayout() || prPage->IsInvalidFlyContent()) &&
+ nullptr != (pObj = lcl_FindFirstInvaObj( prPage, nBottom )) &&
+ pObj->GetObjRect().Top() <= nBottom )
+ {
+ return false;
+ }
+ const SwFrame* pFrame( nullptr );
+ if ( prPage->IsInvalidLayout() &&
+ nullptr != (pFrame = lcl_FindFirstInvaLay( prPage, nBottom )) &&
+ pFrame->getFrameArea().Top() <= nBottom )
+ {
+ return false;
+ }
+ if ( (prPage->IsInvalidContent() || prPage->IsInvalidFlyInCnt()) &&
+ nullptr != (pFrame = lcl_FindFirstInvaContent( prPage, nBottom, nullptr )) &&
+ pFrame->getFrameArea().Top() <= nBottom )
+ {
+ return false;
+ }
+ bRet = true;
+ }
+ return bRet;
+}
+
+// introduce support for vertical layout
+bool SwLayAction::FormatLayout( OutputDevice *pRenderContext, SwLayoutFrame *pLay, bool bAddRect )
+{
+ OSL_ENSURE( !IsAgain(), "Attention to the invalid page." );
+ if ( IsAgain() )
+ return false;
+
+ bool bChanged = false;
+ bool bAlreadyPainted = false;
+ // remember frame at complete paint
+ SwRect aFrameAtCompletePaint;
+
+ if ( !pLay->isFrameAreaDefinitionValid() || pLay->IsCompletePaint() )
+ {
+ if ( pLay->GetPrev() && !pLay->GetPrev()->isFrameAreaDefinitionValid() )
+ pLay->GetPrev()->SetCompletePaint();
+
+ SwRect aOldFrame( pLay->getFrameArea() );
+ SwRect aOldRect( aOldFrame );
+ if( pLay->IsPageFrame() )
+ {
+ aOldRect = static_cast<SwPageFrame*>(pLay)->GetBoundRect(pRenderContext);
+ }
+
+ {
+ SwFrameDeleteGuard aDeleteGuard(pLay);
+ pLay->Calc(pRenderContext);
+ }
+
+ if ( aOldFrame != pLay->getFrameArea() )
+ bChanged = true;
+
+ bool bNoPaint = false;
+ if ( pLay->IsPageBodyFrame() &&
+ pLay->getFrameArea().Pos() == aOldRect.Pos() &&
+ pLay->Lower() )
+ {
+ const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
+ // Limitations because of headers / footers
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() &&
+ !( pLay->IsCompletePaint() && pLay->FindPageFrame()->FindFootnoteCont() ) )
+ bNoPaint = true;
+ }
+
+ if ( !bNoPaint && IsPaint() && bAddRect && (pLay->IsCompletePaint() || bChanged) )
+ {
+ SwRect aPaint( pLay->getFrameArea() );
+ // consider border and shadow for
+ // page frames -> enlarge paint rectangle correspondingly.
+ if ( pLay->IsPageFrame() )
+ {
+ SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(pLay);
+ aPaint = pPageFrame->GetBoundRect(pRenderContext);
+ }
+
+ bool bPageInBrowseMode = pLay->IsPageFrame();
+ if( bPageInBrowseMode )
+ {
+ const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
+ if( !pSh || !pSh->GetViewOptions()->getBrowseMode() )
+ bPageInBrowseMode = false;
+ }
+ if( bPageInBrowseMode )
+ {
+ // NOTE: no vertical layout in online layout
+ // Is the change even visible?
+ if ( pLay->IsCompletePaint() )
+ {
+ m_pImp->GetShell()->AddPaintRect( aPaint );
+ bAddRect = false;
+ }
+ else
+ {
+ SwRegionRects aRegion( aOldRect );
+ aRegion -= aPaint;
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ m_pImp->GetShell()->AddPaintRect( aRegion[i] );
+ aRegion.ChangeOrigin( aPaint );
+ aRegion.clear();
+ aRegion.push_back( aPaint );
+ aRegion -= aOldRect;
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ m_pImp->GetShell()->AddPaintRect( aRegion[i] );
+ }
+ }
+ else
+ {
+ m_pImp->GetShell()->AddPaintRect( aPaint );
+ bAlreadyPainted = true;
+ // remember frame at complete paint
+ aFrameAtCompletePaint = pLay->getFrameArea();
+ }
+
+ // provide paint of spacing
+ // between pages (not only for in online mode).
+ if ( pLay->IsPageFrame() )
+ {
+ const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
+ const SwTwips nHalfDocBorder = pSh ? pSh->GetViewOptions()->GetGapBetweenPages()
+ : SwViewOption::defGapBetweenPages;
+ const bool bLeftToRightViewLayout = m_pRoot->IsLeftToRightViewLayout();
+ const bool bPrev = bLeftToRightViewLayout ? pLay->GetPrev() : pLay->GetNext();
+ const bool bNext = bLeftToRightViewLayout ? pLay->GetNext() : pLay->GetPrev();
+ SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(pLay);
+ SwRect aPageRect( pLay->getFrameArea() );
+
+ if(pSh)
+ {
+ SwPageFrame::GetBorderAndShadowBoundRect(aPageRect, pSh,
+ pRenderContext,
+ aPageRect, pPageFrame->IsLeftShadowNeeded(), pPageFrame->IsRightShadowNeeded(),
+ pPageFrame->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
+ }
+
+ if ( bPrev )
+ {
+ // top
+ SwRect aSpaceToPrevPage( aPageRect );
+ aSpaceToPrevPage.Top( aSpaceToPrevPage.Top() - nHalfDocBorder );
+ aSpaceToPrevPage.Bottom( pLay->getFrameArea().Top() );
+ if(!aSpaceToPrevPage.IsEmpty())
+ m_pImp->GetShell()->AddPaintRect( aSpaceToPrevPage );
+
+ // left
+ aSpaceToPrevPage = aPageRect;
+ aSpaceToPrevPage.Left( aSpaceToPrevPage.Left() - nHalfDocBorder );
+ aSpaceToPrevPage.Right( pLay->getFrameArea().Left() );
+ if(!aSpaceToPrevPage.IsEmpty())
+ m_pImp->GetShell()->AddPaintRect( aSpaceToPrevPage );
+ }
+ if ( bNext )
+ {
+ // bottom
+ SwRect aSpaceToNextPage( aPageRect );
+ aSpaceToNextPage.Bottom( aSpaceToNextPage.Bottom() + nHalfDocBorder );
+ aSpaceToNextPage.Top( pLay->getFrameArea().Bottom() );
+ if(!aSpaceToNextPage.IsEmpty())
+ m_pImp->GetShell()->AddPaintRect( aSpaceToNextPage );
+
+ // right
+ aSpaceToNextPage = aPageRect;
+ aSpaceToNextPage.Right( aSpaceToNextPage.Right() + nHalfDocBorder );
+ aSpaceToNextPage.Left( pLay->getFrameArea().Right() );
+ if(!aSpaceToNextPage.IsEmpty())
+ m_pImp->GetShell()->AddPaintRect( aSpaceToNextPage );
+ }
+ }
+ }
+ pLay->ResetCompletePaint();
+ }
+
+ if ( IsPaint() && bAddRect &&
+ !pLay->GetNext() && pLay->IsRetoucheFrame() && pLay->IsRetouche() )
+ {
+ // vertical layout support
+ SwRectFnSet aRectFnSet(pLay);
+ SwRect aRect( pLay->GetUpper()->GetPaintArea() );
+ aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pLay) );
+ if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
+ pLay->ResetRetouche();
+ }
+
+ if( bAlreadyPainted )
+ bAddRect = false;
+
+ CheckWaitCursor();
+
+ if ( IsAgain() )
+ return false;
+
+ // Now, deal with the lowers that are LayoutFrames
+
+ if ( pLay->IsFootnoteFrame() ) // no LayFrames as Lower
+ return bChanged;
+
+ SwFrame *pLow = pLay->Lower();
+ bool bTabChanged = false;
+ while ( pLow && pLow->GetUpper() == pLay )
+ {
+ SwFrame* pNext = nullptr;
+ if ( pLow->IsLayoutFrame() )
+ {
+ if ( pLow->IsTabFrame() )
+ {
+ // Remember what was the next of the lower. Formatting may move it to the previous
+ // page, in which case it looses its next.
+ pNext = pLow->GetNext();
+
+ if (pNext && pNext->IsTabFrame())
+ {
+ auto pTab = static_cast<SwTabFrame*>(pNext);
+ if (pTab->IsFollow())
+ {
+ // The next frame is a follow of the previous frame, SwTabFrame::Join() will
+ // delete this one as part of formatting, so forget about it.
+ pNext = nullptr;
+ }
+ }
+
+ bTabChanged |= FormatLayoutTab( static_cast<SwTabFrame*>(pLow), bAddRect );
+ }
+ // Skip the ones already registered for deletion
+ else if( !pLow->IsSctFrame() || static_cast<SwSectionFrame*>(pLow)->GetSection() )
+ {
+ PushFormatLayout(pLow);
+ bChanged |= FormatLayout( pRenderContext, static_cast<SwLayoutFrame*>(pLow), bAddRect );
+ PopFormatLayout();
+ }
+ }
+ else if ( m_pImp->GetShell()->IsPaintLocked() )
+ // Shortcut to minimize the cycles. With Lock, the
+ // paint is coming either way (primarily for browse)
+ pLow->OptCalc();
+
+ if ( IsAgain() )
+ return false;
+ if (!pNext)
+ {
+ pNext = pLow->GetNext();
+ }
+ pLow = pNext;
+ }
+ // add complete frame area as paint area, if frame
+ // area has been already added and after formatting its lowers the frame area
+ // is enlarged.
+ SwRect aBoundRect(pLay->IsPageFrame() ? static_cast<SwPageFrame*>(pLay)->GetBoundRect(pRenderContext) : pLay->getFrameArea() );
+
+ if ( bAlreadyPainted &&
+ ( aBoundRect.Width() > aFrameAtCompletePaint.Width() ||
+ aBoundRect.Height() > aFrameAtCompletePaint.Height() )
+ )
+ {
+ m_pImp->GetShell()->AddPaintRect( aBoundRect );
+ }
+ return bChanged || bTabChanged;
+}
+
+void SwLayAction::FormatLayoutFly( SwFlyFrame* pFly )
+{
+ vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
+ OSL_ENSURE( !IsAgain(), "Attention to the invalid page." );
+ if ( IsAgain() )
+ return;
+
+ bool bChanged = false;
+ bool bAddRect = true;
+
+ if ( !pFly->isFrameAreaDefinitionValid() || pFly->IsCompletePaint() || pFly->IsInvalid() )
+ {
+ // The Frame has changed, now it's getting formatted.
+ const SwRect aOldRect( pFly->getFrameArea() );
+ pFly->Calc(pRenderContext);
+ bChanged = aOldRect != pFly->getFrameArea();
+
+ if ( IsPaint() && (pFly->IsCompletePaint() || bChanged) &&
+ pFly->getFrameArea().Top() > 0 && pFly->getFrameArea().Left() > 0 )
+ m_pImp->GetShell()->AddPaintRect( pFly->getFrameArea() );
+
+ if ( bChanged )
+ pFly->Invalidate();
+ else
+ pFly->Validate();
+
+ bAddRect = false;
+ pFly->ResetCompletePaint();
+ }
+
+ if ( IsAgain() )
+ return;
+
+ // Now, deal with the lowers that are LayoutFrames
+ SwFrame *pLow = pFly->Lower();
+ while ( pLow )
+ {
+ if ( pLow->IsLayoutFrame() )
+ {
+ if ( pLow->IsTabFrame() )
+ FormatLayoutTab( static_cast<SwTabFrame*>(pLow), bAddRect );
+ else
+ FormatLayout( m_pImp->GetShell()->GetOut(), static_cast<SwLayoutFrame*>(pLow), bAddRect );
+ }
+ pLow = pLow->GetNext();
+ }
+}
+
+// Implement vertical layout support
+bool SwLayAction::FormatLayoutTab( SwTabFrame *pTab, bool bAddRect )
+{
+ OSL_ENSURE( !IsAgain(), "8-) Attention to the invalid page." );
+ if ( IsAgain() || !pTab->Lower() )
+ return false;
+
+ vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
+ IDocumentTimerAccess& rTimerAccess = m_pRoot->GetFormat()->getIDocumentTimerAccess();
+ rTimerAccess.BlockIdling();
+
+ bool bChanged = false;
+ bool bPainted = false;
+
+ const SwPageFrame *pOldPage = pTab->FindPageFrame();
+
+ // vertical layout support
+ SwRectFnSet aRectFnSet(pTab);
+
+ if ( !pTab->isFrameAreaDefinitionValid() || pTab->IsCompletePaint() || pTab->IsComplete() )
+ {
+ if ( pTab->GetPrev() && !pTab->GetPrev()->isFrameAreaDefinitionValid() )
+ {
+ pTab->GetPrev()->SetCompletePaint();
+ }
+
+ const SwRect aOldRect( pTab->getFrameArea() );
+ pTab->SetLowersFormatted( false );
+ pTab->Calc(pRenderContext);
+ if ( aOldRect != pTab->getFrameArea() )
+ {
+ bChanged = true;
+ }
+ const SwRect aPaintFrame = pTab->GetPaintArea();
+
+ if ( IsPaint() && bAddRect )
+ {
+ // add condition <pTab->getFrameArea().HasArea()>
+ if ( !pTab->IsCompletePaint() &&
+ pTab->IsComplete() &&
+ ( pTab->getFrameArea().SSize() != pTab->getFramePrintArea().SSize() ||
+ // vertical layout support
+ aRectFnSet.GetLeftMargin(*pTab) ) &&
+ pTab->getFrameArea().HasArea()
+ )
+ {
+ // re-implement calculation of margin rectangles.
+ SwRect aMarginRect;
+
+ SwTwips nLeftMargin = aRectFnSet.GetLeftMargin(*pTab);
+ if ( nLeftMargin > 0)
+ {
+ aMarginRect = pTab->getFrameArea();
+ aRectFnSet.SetWidth( aMarginRect, nLeftMargin );
+ m_pImp->GetShell()->AddPaintRect( aMarginRect );
+ }
+
+ if ( aRectFnSet.GetRightMargin(*pTab) > 0)
+ {
+ aMarginRect = pTab->getFrameArea();
+ aRectFnSet.SetLeft( aMarginRect, aRectFnSet.GetPrtRight(*pTab) );
+ m_pImp->GetShell()->AddPaintRect( aMarginRect );
+ }
+
+ SwTwips nTopMargin = aRectFnSet.GetTopMargin(*pTab);
+ if ( nTopMargin > 0)
+ {
+ aMarginRect = pTab->getFrameArea();
+ aRectFnSet.SetHeight( aMarginRect, nTopMargin );
+ m_pImp->GetShell()->AddPaintRect( aMarginRect );
+ }
+
+ if ( aRectFnSet.GetBottomMargin(*pTab) > 0)
+ {
+ aMarginRect = pTab->getFrameArea();
+ aRectFnSet.SetTop( aMarginRect, aRectFnSet.GetPrtBottom(*pTab) );
+ m_pImp->GetShell()->AddPaintRect( aMarginRect );
+ }
+ }
+ else if ( pTab->IsCompletePaint() )
+ {
+ m_pImp->GetShell()->AddPaintRect( aPaintFrame );
+ bAddRect = false;
+ bPainted = true;
+ }
+
+ if ( pTab->IsRetouche() && !pTab->GetNext() )
+ {
+ SwRect aRect( pTab->GetUpper()->GetPaintArea() );
+ // vertical layout support
+ aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTab) );
+ if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
+ pTab->ResetRetouche();
+ }
+ }
+ else
+ bAddRect = false;
+
+ if ( pTab->IsCompletePaint() && !m_pOptTab )
+ m_pOptTab = pTab;
+ pTab->ResetCompletePaint();
+ }
+ if ( IsPaint() && bAddRect && pTab->IsRetouche() && !pTab->GetNext() )
+ {
+ // set correct rectangle for retouche: area between bottom of table frame
+ // and bottom of paint area of the upper frame.
+ SwRect aRect( pTab->GetUpper()->GetPaintArea() );
+ // vertical layout support
+ aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTab) );
+ if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
+ pTab->ResetRetouche();
+ }
+
+ CheckWaitCursor();
+
+ rTimerAccess.UnblockIdling();
+
+ // Ugly shortcut!
+ if ( pTab->IsLowersFormatted() &&
+ (bPainted || !m_pImp->GetShell()->VisArea().Overlaps( pTab->getFrameArea())) )
+ return false;
+
+ // Now, deal with the lowers
+ if ( IsAgain() )
+ return false;
+
+ // for safety reasons:
+ // check page number before formatting lowers.
+ if ( pOldPage->GetPhyPageNum() > (pTab->FindPageFrame()->GetPhyPageNum() + 1) )
+ SetNextCycle( true );
+
+ // format lowers, only if table frame is valid
+ if ( pTab->isFrameAreaDefinitionValid() )
+ {
+ FlowFrameJoinLockGuard tabG(pTab); // tdf#124675 prevent Join() if pTab becomes empty
+ SwLayoutFrame *pLow = static_cast<SwLayoutFrame*>(pTab->Lower());
+ while ( pLow )
+ {
+ SwFrameDeleteGuard rowG(pLow); // tdf#124675 prevent RemoveFollowFlowLine()
+ bChanged |= FormatLayout( m_pImp->GetShell()->GetOut(), pLow, bAddRect );
+ if ( IsAgain() )
+ return false;
+ pLow = static_cast<SwLayoutFrame*>(pLow->GetNext());
+ }
+ }
+
+ return bChanged;
+}
+
+bool SwLayAction::FormatContent(SwPageFrame *const pPage)
+{
+ ::comphelper::ScopeGuard g([this, pPage]() {
+ if (IsAgain())
+ {
+ return; // pPage probably deleted
+ }
+ if (auto const* pObjs = pPage->GetSortedObjs())
+ {
+ std::vector<std::pair<SwAnchoredObject*, SwPageFrame*>> moved;
+ for (auto const pObj : *pObjs)
+ {
+ assert(!pObj->AnchorFrame()->IsTextFrame()
+ || !static_cast<SwTextFrame const*>(pObj->AnchorFrame())->IsFollow());
+ SwPageFrame *const pAnchorPage(pObj->AnchorFrame()->FindPageFrame());
+ assert(pAnchorPage);
+ if (pAnchorPage != pPage
+ && pPage->GetPhyPageNum() < pAnchorPage->GetPhyPageNum()
+ && pObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ != RndStdIds::FLY_AS_CHAR)
+ {
+ moved.emplace_back(pObj, pAnchorPage);
+ }
+ }
+ for (auto const& [pObj, pAnchorPage] : moved)
+ {
+ SAL_INFO("sw.layout", "SwLayAction::FormatContent: move anchored " << pObj << " from " << pPage->GetPhyPageNum() << " to " << pAnchorPage->GetPhyPageNum());
+ pObj->RegisterAtPage(*pAnchorPage);
+ // tdf#143239 if the position remains valid, it may not be
+ // positioned again so would remain on the wrong page!
+ pObj->InvalidateObjPos();
+ ::Notify_Background(pObj->GetDrawObj(), pPage,
+ pObj->GetObjRect(), PrepareHint::FlyFrameLeave, false);
+ }
+ if (!moved.empty())
+ {
+ pPage->InvalidateFlyLayout();
+ if (auto *const pContent = pPage->FindLastBodyContent())
+ {
+ pContent->InvalidateSize();
+ }
+ }
+ }
+ });
+
+ const SwContentFrame *pContent = pPage->ContainsContent();
+ const SwViewShell *pSh = m_pRoot->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+
+ while ( pContent && pPage->IsAnLower( pContent ) )
+ {
+ // If the content didn't change, we can use a few shortcuts.
+ const bool bFull = !pContent->isFrameAreaDefinitionValid() || pContent->IsCompletePaint() ||
+ pContent->IsRetouche() || pContent->GetDrawObjs();
+ if ( bFull )
+ {
+ // We do this so we don't have to search later on.
+ const bool bNxtCnt = IsCalcLayout() && !pContent->GetFollow();
+ const SwContentFrame *pContentNext = bNxtCnt ? pContent->GetNextContentFrame() : nullptr;
+ SwContentFrame* const pContentPrev = pContent->GetPrev() ? pContent->GetPrevContentFrame() : nullptr;
+ std::optional<SfxDeleteListener> oPrevDeleteListener;
+ if (pContentPrev)
+ oPrevDeleteListener.emplace(*pContentPrev);
+
+ const SwLayoutFrame*pOldUpper = pContent->GetUpper();
+ const SwTabFrame *pTab = pContent->FindTabFrame();
+ const bool bInValid = !pContent->isFrameAreaDefinitionValid() || pContent->IsCompletePaint();
+ const bool bOldPaint = IsPaint();
+ m_bPaint = bOldPaint && !(pTab && pTab == m_pOptTab);
+ FormatContent_( pContent, pPage );
+ // reset <bPaint> before format objects
+ m_bPaint = bOldPaint;
+
+ // format floating screen object at content frame.
+ // No format, if action flag <bAgain> is set or action is interrupted.
+ // allow format on interruption of action, if
+ // it's the format for this interrupt
+ // pass correct page frame
+ // to the object formatter.
+ if ( !IsAgain() &&
+ ( !IsInterrupt() || mbFormatContentOnInterrupt ) &&
+ pContent->IsTextFrame() &&
+ !SwObjectFormatter::FormatObjsAtFrame( *const_cast<SwContentFrame*>(pContent),
+ *(pContent->FindPageFrame()), this ) )
+ {
+ return false;
+ }
+
+ if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
+ {
+ const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
+ if ( IsPaintExtraData() && IsPaint() &&
+ nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
+ m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
+ }
+
+ if ( IsAgain() )
+ return false;
+
+ // Temporarily interrupt processing if layout or Flys become invalid again.
+ // However not for the BrowseView: The layout is getting invalid
+ // all the time because the page height gets adjusted.
+ // The same applies if the user wants to continue working and at least one
+ // paragraph has been processed.
+ if (!pTab || !bInValid)
+ {
+ CheckIdleEnd();
+ // consider interrupt formatting.
+ if ( ( IsInterrupt() && !mbFormatContentOnInterrupt ) ||
+ ( !bBrowse && pPage->IsInvalidLayout() ) ||
+ // consider interrupt formatting
+ ( pPage->GetSortedObjs() && pPage->IsInvalidFly() && !mbFormatContentOnInterrupt )
+ )
+ return false;
+ }
+ if ( pOldUpper != pContent->GetUpper() )
+ {
+ const sal_uInt16 nCurNum = pContent->FindPageFrame()->GetPhyPageNum();
+ if ( nCurNum < pPage->GetPhyPageNum() )
+ m_nPreInvaPage = nCurNum;
+
+ // If the frame flowed backwards more than one page, we need to
+ // start over again from the beginning, so nothing gets left out.
+ if ( !IsCalcLayout() && pPage->GetPhyPageNum() > nCurNum+1 )
+ {
+ SetNextCycle( true );
+ // consider interrupt formatting
+ if ( !mbFormatContentOnInterrupt )
+ {
+ return false;
+ }
+ }
+ }
+ // If the frame moved forwards to the next page, we re-run through
+ // the predecessor.
+ // This way, we catch predecessors which are now responsible for
+ // retouching, but the footers will be touched also.
+ bool bSetContent = true;
+ if ( pContentPrev )
+ {
+ if (oPrevDeleteListener->WasDeleted())
+ {
+ SAL_WARN("sw", "ContentPrev was deleted");
+ return false;
+ }
+
+ if ( !pContentPrev->isFrameAreaDefinitionValid() && pPage->IsAnLower( pContentPrev ) )
+ {
+ pPage->InvalidateContent();
+ }
+
+ if ( pOldUpper != pContent->GetUpper() &&
+ pPage->GetPhyPageNum() < pContent->FindPageFrame()->GetPhyPageNum() )
+ {
+ pContent = pContentPrev;
+ bSetContent = false;
+ }
+ }
+ if ( bSetContent )
+ {
+ if ( bBrowse && !IsIdle() && !IsCalcLayout() && !IsComplete() &&
+ pContent->getFrameArea().Top() > m_pImp->GetShell()->VisArea().Bottom())
+ {
+ const tools::Long nBottom = m_pImp->GetShell()->VisArea().Bottom();
+ const SwFrame *pTmp = lcl_FindFirstInvaContent( pPage,
+ nBottom, pContent );
+ if ( !pTmp )
+ {
+ if ( (!(pPage->GetSortedObjs() && pPage->IsInvalidFly()) ||
+ !lcl_FindFirstInvaObj( pPage, nBottom )) &&
+ (!pPage->IsInvalidLayout() ||
+ !lcl_FindFirstInvaLay( pPage, nBottom )))
+ SetBrowseActionStop( true );
+ // consider interrupt formatting.
+ if ( !mbFormatContentOnInterrupt )
+ {
+ return false;
+ }
+ }
+ }
+ pContent = bNxtCnt ? pContentNext : pContent->GetNextContentFrame();
+ }
+
+ if (IsReschedule())
+ {
+ ::RescheduleProgress(m_pImp->GetShell()->GetDoc()->GetDocShell());
+ }
+ }
+ else
+ {
+ if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
+ {
+ const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
+ if ( IsPaintExtraData() && IsPaint() &&
+ nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
+ m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
+ }
+
+ // Do this if the frame has been formatted before.
+ if ( pContent->IsTextFrame() && static_cast<const SwTextFrame*>(pContent)->HasRepaint() &&
+ IsPaint() )
+ PaintContent( pContent, pPage, pContent->getFrameArea(), pContent->getFrameArea().Bottom());
+ if ( IsIdle() )
+ {
+ CheckIdleEnd();
+ // consider interrupt formatting.
+ if ( IsInterrupt() && !mbFormatContentOnInterrupt )
+ return false;
+ }
+ if ( bBrowse && !IsIdle() && !IsCalcLayout() && !IsComplete() &&
+ pContent->getFrameArea().Top() > m_pImp->GetShell()->VisArea().Bottom())
+ {
+ const tools::Long nBottom = m_pImp->GetShell()->VisArea().Bottom();
+ const SwFrame *pTmp = lcl_FindFirstInvaContent( pPage,
+ nBottom, pContent );
+ if ( !pTmp )
+ {
+ if ( (!(pPage->GetSortedObjs() && pPage->IsInvalidFly()) ||
+ !lcl_FindFirstInvaObj( pPage, nBottom )) &&
+ (!pPage->IsInvalidLayout() ||
+ !lcl_FindFirstInvaLay( pPage, nBottom )))
+ SetBrowseActionStop( true );
+ // consider interrupt formatting.
+ if ( !mbFormatContentOnInterrupt )
+ {
+ return false;
+ }
+ }
+ }
+ pContent = pContent->GetNextContentFrame();
+ }
+ }
+ CheckWaitCursor();
+ // consider interrupt formatting.
+ return !IsInterrupt() || mbFormatContentOnInterrupt;
+}
+
+void SwLayAction::FormatContent_( const SwContentFrame *pContent, const SwPageFrame *pPage )
+{
+ // We probably only ended up here because the Content holds DrawObjects.
+ const bool bDrawObjsOnly = pContent->isFrameAreaDefinitionValid() && !pContent->IsCompletePaint() && !pContent->IsRetouche();
+ SwRectFnSet aRectFnSet(pContent);
+ if ( !bDrawObjsOnly && IsPaint() )
+ {
+ const SwRect aOldRect( pContent->UnionFrame() );
+ const tools::Long nOldBottom = aRectFnSet.GetPrtBottom(*pContent);
+ pContent->OptCalc();
+ if( IsAgain() )
+ return;
+ if( aRectFnSet.YDiff( aRectFnSet.GetBottom(pContent->getFrameArea()),
+ aRectFnSet.GetBottom(aOldRect) ) < 0 )
+ {
+ pContent->SetRetouche();
+ }
+ PaintContent( pContent, pContent->FindPageFrame(), aOldRect, nOldBottom);
+ }
+ else
+ {
+ if ( IsPaint() && pContent->IsTextFrame() && static_cast<const SwTextFrame*>(pContent)->HasRepaint() )
+ PaintContent( pContent, pPage, pContent->getFrameArea(),
+ aRectFnSet.GetBottom(pContent->getFrameArea()) );
+ pContent->OptCalc();
+ }
+}
+
+void SwLayAction::FormatFlyContent( const SwFlyFrame *pFly )
+{
+ const SwContentFrame *pContent = pFly->ContainsContent();
+
+ while ( pContent )
+ {
+ FormatContent_( pContent, pContent->FindPageFrame() );
+
+ // format floating screen objects at content text frame
+ // pass correct page frame to the object formatter.
+ if ( pContent->IsTextFrame() &&
+ !SwObjectFormatter::FormatObjsAtFrame(
+ *const_cast<SwContentFrame*>(pContent),
+ *(pContent->FindPageFrame()), this ) )
+ {
+ // restart format with first content
+ pContent = pFly->ContainsContent();
+ continue;
+ }
+
+ if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
+ {
+ const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
+ if ( IsPaintExtraData() && IsPaint() &&
+ nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
+ m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
+ }
+
+ if ( IsAgain() )
+ return;
+
+ // If there's input, we interrupt processing.
+ if ( !pFly->IsFlyInContentFrame() )
+ {
+ CheckIdleEnd();
+ // consider interrupt formatting.
+ if ( IsInterrupt() && !mbFormatContentOnInterrupt )
+ return;
+ }
+ pContent = pContent->GetNextContentFrame();
+ }
+ CheckWaitCursor();
+}
+
+bool SwLayIdle::DoIdleJob_( const SwContentFrame *pCnt, IdleJobType eJob )
+{
+ OSL_ENSURE( pCnt->IsTextFrame(), "NoText neighbour of Text" );
+ // robust against misuse by e.g. #i52542#
+ if( !pCnt->IsTextFrame() )
+ return false;
+
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pCnt));
+ // sw_redlinehide: spell check only the nodes with visible content?
+ SwTextNode* pTextNode = const_cast<SwTextNode*>(pTextFrame->GetTextNodeForParaProps());
+
+ bool bProcess = false;
+ for (size_t i = 0; pTextNode; )
+ {
+ switch ( eJob )
+ {
+ case ONLINE_SPELLING :
+ bProcess = pTextNode->IsWrongDirty(); break;
+ case AUTOCOMPLETE_WORDS :
+ bProcess = pTextNode->IsAutoCompleteWordDirty(); break;
+ case WORD_COUNT :
+ bProcess = pTextNode->IsWordCountDirty(); break;
+ case SMART_TAGS :
+ bProcess = pTextNode->IsSmartTagDirty(); break;
+ }
+ if (bProcess)
+ {
+ break;
+ }
+ if (sw::MergedPara const* pMerged = pTextFrame->GetMergedPara())
+ {
+ while (true)
+ {
+ ++i;
+ if (i < pMerged->extents.size())
+ {
+ if (pMerged->extents[i].pNode != pTextNode)
+ {
+ pTextNode = pMerged->extents[i].pNode;
+ break;
+ }
+ }
+ else
+ {
+ pTextNode = nullptr;
+ break;
+ }
+ }
+ }
+ else
+ pTextNode = nullptr;
+ }
+
+ if( bProcess )
+ {
+ assert(pTextNode);
+ SwViewShell *pSh = m_pImp->GetShell();
+ if( COMPLETE_STRING == m_nTextPos )
+ {
+ --m_nTextPos;
+ if( auto pCursorShell = dynamic_cast<SwCursorShell *>( pSh ) )
+ if( !pCursorShell->IsTableMode() )
+ {
+ SwPaM *pCursor = pCursorShell->GetCursor();
+ if( !pCursor->HasMark() && !pCursor->IsMultiSelection() )
+ {
+ m_pContentNode = pCursor->GetContentNode();
+ m_nTextPos = pCursor->GetPoint()->nContent.GetIndex();
+ }
+ }
+ }
+ sal_Int32 const nPos((m_pContentNode && pTextNode == m_pContentNode)
+ ? m_nTextPos
+ : COMPLETE_STRING);
+
+ switch ( eJob )
+ {
+ case ONLINE_SPELLING :
+ {
+ SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->AutoSpell_(*pTextNode, nPos) );
+ // PENDING should stop idle spell checking
+ m_bPageValid = m_bPageValid && (SwTextNode::WrongState::TODO != pTextNode->GetWrongDirty());
+ if ( aRepaint.HasArea() )
+ m_pImp->GetShell()->InvalidateWindows( aRepaint );
+ if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
+ return true;
+ break;
+ }
+ case AUTOCOMPLETE_WORDS :
+ const_cast<SwTextFrame*>(pTextFrame)->CollectAutoCmplWrds(*pTextNode, nPos);
+ // note: bPageValid remains true here even if the cursor
+ // position is skipped, so no PENDING state needed currently
+ if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
+ return true;
+ break;
+ case WORD_COUNT :
+ {
+ const sal_Int32 nEnd = pTextNode->GetText().getLength();
+ SwDocStat aStat;
+ pTextNode->CountWords( aStat, 0, nEnd );
+ if ( Application::AnyInput() )
+ return true;
+ break;
+ }
+ case SMART_TAGS :
+ {
+ try {
+ const SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->SmartTagScan(*pTextNode) );
+ m_bPageValid = m_bPageValid && !pTextNode->IsSmartTagDirty();
+ if ( aRepaint.HasArea() )
+ m_pImp->GetShell()->InvalidateWindows( aRepaint );
+ } catch( const css::uno::RuntimeException&) {
+ // handle smarttag problems gracefully and provide diagnostics
+ TOOLS_WARN_EXCEPTION( "sw.core", "SMART_TAGS");
+ }
+ if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
+ return true;
+ break;
+ }
+ }
+ }
+
+ // The Flys that are anchored to the paragraph need to be considered too.
+ if ( pCnt->GetDrawObjs() )
+ {
+ const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ const SwContentFrame *pC = pFly->ContainsContent();
+ while( pC )
+ {
+ if ( pC->IsTextFrame() )
+ {
+ if ( DoIdleJob_( pC, eJob ) )
+ return true;
+ }
+ pC = pC->GetNextContentFrame();
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool SwLayIdle::DoIdleJob( IdleJobType eJob, bool bVisAreaOnly )
+{
+ // Spellcheck all contents of the pages. Either only the
+ // visible ones or all of them.
+ const SwViewShell* pViewShell = m_pImp->GetShell();
+ const SwViewOption* pViewOptions = pViewShell->GetViewOptions();
+ const SwDoc* pDoc = pViewShell->GetDoc();
+
+ switch ( eJob )
+ {
+ case ONLINE_SPELLING :
+ if( !pViewOptions->IsOnlineSpell() )
+ return false;
+ break;
+ case AUTOCOMPLETE_WORDS :
+ if( !SwViewOption::IsAutoCompleteWords() ||
+ SwDoc::GetAutoCompleteWords().IsLockWordLstLocked())
+ return false;
+ break;
+ case WORD_COUNT :
+ if ( !pViewShell->getIDocumentStatistics().GetDocStat().bModified )
+ return false;
+ break;
+ case SMART_TAGS :
+ if ( pDoc->GetDocShell()->IsHelpDocument() ||
+ pDoc->isXForms() ||
+ !SwSmartTagMgr::Get().IsSmartTagsEnabled() )
+ return false;
+ break;
+ default: OSL_FAIL( "Unknown idle job type" );
+ }
+
+ SwPageFrame *pPage;
+ if ( bVisAreaOnly )
+ pPage = m_pImp->GetFirstVisPage(pViewShell->GetOut());
+ else
+ pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
+
+ m_pContentNode = nullptr;
+ m_nTextPos = COMPLETE_STRING;
+
+ while ( pPage )
+ {
+ m_bPageValid = true;
+ const SwContentFrame *pCnt = pPage->ContainsContent();
+ while( pCnt && pPage->IsAnLower( pCnt ) )
+ {
+ if ( DoIdleJob_( pCnt, eJob ) )
+ {
+ SAL_INFO("sw.idle", "DoIdleJob " << eJob << " interrupted on page " << pPage->GetPhyPageNum());
+ return true;
+ }
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ if ( pPage->GetSortedObjs() )
+ {
+ for ( size_t i = 0; pPage->GetSortedObjs() &&
+ i < pPage->GetSortedObjs()->size(); ++i )
+ {
+ const SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i];
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ const SwContentFrame *pC = pFly->ContainsContent();
+ while( pC )
+ {
+ if ( pC->IsTextFrame() )
+ {
+ if ( DoIdleJob_( pC, eJob ) )
+ {
+ SAL_INFO("sw.idle", "DoIdleJob " << eJob << " interrupted on page " << pPage->GetPhyPageNum());
+ return true;
+ }
+ }
+ pC = pC->GetNextContentFrame();
+ }
+ }
+ }
+ }
+
+ if( m_bPageValid )
+ {
+ switch ( eJob )
+ {
+ case ONLINE_SPELLING : pPage->ValidateSpelling(); break;
+ case AUTOCOMPLETE_WORDS : pPage->ValidateAutoCompleteWords(); break;
+ case WORD_COUNT : pPage->ValidateWordCount(); break;
+ case SMART_TAGS : pPage->ValidateSmartTags(); break;
+ }
+ }
+
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ if ( pPage && bVisAreaOnly &&
+ !pPage->getFrameArea().Overlaps( m_pImp->GetShell()->VisArea()))
+ break;
+ }
+ return false;
+}
+
+#if HAVE_FEATURE_DESKTOP && defined DBG_UTIL
+void SwLayIdle::ShowIdle( Color eColor )
+{
+ if ( m_bIndicator )
+ return;
+
+ m_bIndicator = true;
+ vcl::Window *pWin = m_pImp->GetShell()->GetWin();
+ if (pWin && !pWin->SupportsDoubleBuffering()) // FIXME make this work with double-buffering
+ {
+ tools::Rectangle aRect( 0, 0, 5, 5 );
+ aRect = pWin->PixelToLogic( aRect );
+ // Depending on if idle layout is in progress or not, draw a "red square" or a "green square".
+ pWin->GetOutDev()->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ pWin->GetOutDev()->SetFillColor( eColor );
+ pWin->GetOutDev()->SetLineColor();
+ pWin->GetOutDev()->DrawRect( aRect );
+ pWin->GetOutDev()->Pop();
+ }
+}
+#define SHOW_IDLE( Color ) ShowIdle( Color )
+#else
+#define SHOW_IDLE( Color )
+#endif // DBG_UTIL
+
+SwLayIdle::SwLayIdle( SwRootFrame *pRt, SwViewShellImp *pI ) :
+ m_pRoot( pRt ),
+ m_pImp( pI )
+#ifdef DBG_UTIL
+ , m_bIndicator( false )
+#endif
+{
+ SAL_INFO("sw.idle", "SwLayIdle() entry");
+
+ m_pImp->m_pIdleAct = this;
+
+ SHOW_IDLE( COL_LIGHTRED );
+
+ m_pImp->GetShell()->EnableSmooth( false );
+
+ // First, spellcheck the visible area. Only if there's nothing
+ // to do there, we trigger the IdleFormat.
+ if ( !DoIdleJob( SMART_TAGS, true ) &&
+ !DoIdleJob( ONLINE_SPELLING, true ) &&
+ !DoIdleJob( AUTOCOMPLETE_WORDS, true ) )
+ {
+ // Format, then register repaint rectangles with the SwViewShell if necessary.
+ // This requires running artificial actions, so we don't get undesired
+ // effects when for instance the page count gets changed.
+ // We remember the shells where the cursor is visible, so we can make
+ // it visible again if needed after a document change.
+ std::vector<bool> aBools;
+ for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
+ {
+ ++rSh.mnStartAction;
+ bool bVis = false;
+ if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
+ {
+ bVis = pCursorShell->GetCharRect().Overlaps(rSh.VisArea());
+ }
+ aBools.push_back( bVis );
+ }
+
+ bool bInterrupt(false);
+ {
+ SwLayAction aAction( m_pRoot, m_pImp );
+ aAction.SetInputType( VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER) );
+ aAction.SetIdle( true );
+ aAction.SetWaitAllowed( false );
+ aAction.Action(m_pImp->GetShell()->GetOut());
+ bInterrupt = aAction.IsInterrupt();
+ }
+
+ // Further start/end actions only happen if there were paints started
+ // somewhere or if the visibility of the CharRects has changed.
+ bool bActions = false;
+ size_t nBoolIdx = 0;
+ for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
+ {
+ --rSh.mnStartAction;
+
+ if ( rSh.Imp()->HasPaintRegion() )
+ bActions = true;
+ else
+ {
+ SwRect aTmp( rSh.VisArea() );
+ rSh.UISizeNotify();
+
+ // Are we supposed to crash if rSh isn't a cursor shell?!
+ // bActions |= aTmp != rSh.VisArea() ||
+ // aBools[nBoolIdx] != ((SwCursorShell*)&rSh)->GetCharRect().IsOver( rSh.VisArea() );
+
+ // aBools[ i ] is true, if the i-th shell is a cursor shell (!!!)
+ // and the cursor is visible.
+ bActions |= aTmp != rSh.VisArea();
+ if ( aTmp == rSh.VisArea() )
+ if ( auto pCursorShell = dynamic_cast< SwCursorShell*>( &rSh) )
+ bActions |= aBools[nBoolIdx] != pCursorShell->GetCharRect().Overlaps( rSh.VisArea() );
+ }
+
+ ++nBoolIdx;
+ }
+
+ if ( bActions )
+ {
+ // Prepare start/end actions via CursorShell, so the cursor, selection
+ // and VisArea can be set correctly.
+ nBoolIdx = 0;
+ for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
+ {
+ SwCursorShell* pCursorShell = dynamic_cast<SwCursorShell*>( &rSh);
+
+ if ( pCursorShell )
+ pCursorShell->SttCursorMove();
+
+ // If there are accrued paints, it's best to simply invalidate
+ // the whole window. Otherwise there would arise paint problems whose
+ // solution would be disproportionally expensive.
+ SwViewShellImp *pViewImp = rSh.Imp();
+ bool bUnlock = false;
+ if ( pViewImp->HasPaintRegion() )
+ {
+ pViewImp->DeletePaintRegion();
+
+ // Cause a repaint with virtual device.
+ rSh.LockPaint();
+ bUnlock = true;
+ }
+
+ if ( pCursorShell )
+ // If the Cursor was visible, we need to make it visible again.
+ // Otherwise, EndCursorMove with true for IdleEnd
+ pCursorShell->EndCursorMove( !aBools[nBoolIdx] );
+ if( bUnlock )
+ {
+ if( pCursorShell )
+ {
+ // UnlockPaint overwrite the selection from the
+ // CursorShell and calls the virtual method paint
+ // to fill the virtual device. This fill don't have
+ // paint the selection! -> Set the focus flag at
+ // CursorShell and it doesn't paint the selection.
+ pCursorShell->ShellLoseFocus();
+ pCursorShell->UnlockPaint( true );
+ pCursorShell->ShellGetFocus();
+ }
+ else
+ rSh.UnlockPaint( true );
+ }
+ ++nBoolIdx;
+
+ }
+ }
+
+ if (!bInterrupt)
+ {
+ if ( !DoIdleJob( WORD_COUNT, false ) )
+ if ( !DoIdleJob( SMART_TAGS, false ) )
+ if ( !DoIdleJob( ONLINE_SPELLING, false ) )
+ DoIdleJob( AUTOCOMPLETE_WORDS, false );
+ }
+
+ bool bInValid = false;
+ const SwViewOption& rVOpt = *m_pImp->GetShell()->GetViewOptions();
+ const SwViewShell* pViewShell = m_pImp->GetShell();
+ // See conditions in DoIdleJob()
+ const bool bSpell = rVOpt.IsOnlineSpell();
+ const bool bACmplWrd = SwViewOption::IsAutoCompleteWords();
+ const bool bWordCount = pViewShell->getIDocumentStatistics().GetDocStat().bModified;
+ const bool bSmartTags = !pViewShell->GetDoc()->GetDocShell()->IsHelpDocument() &&
+ !pViewShell->GetDoc()->isXForms() &&
+ SwSmartTagMgr::Get().IsSmartTagsEnabled();
+
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
+ do
+ {
+ bInValid = pPg->IsInvalidContent() || pPg->IsInvalidLayout() ||
+ pPg->IsInvalidFlyContent() || pPg->IsInvalidFlyLayout() ||
+ pPg->IsInvalidFlyInCnt() ||
+ (bSpell && pPg->IsInvalidSpelling()) ||
+ (bACmplWrd && pPg->IsInvalidAutoCompleteWords()) ||
+ (bWordCount && pPg->IsInvalidWordCount()) ||
+ (bSmartTags && pPg->IsInvalidSmartTags());
+
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+
+ } while ( pPg && !bInValid );
+
+ if ( !bInValid )
+ {
+ m_pRoot->ResetIdleFormat();
+ SfxObjectShell* pDocShell = m_pImp->GetShell()->GetDoc()->GetDocShell();
+ pDocShell->Broadcast( SfxEventHint( SfxEventHintId::SwEventLayoutFinished, SwDocShell::GetEventName(STR_SW_EVENT_LAYOUT_FINISHED), pDocShell ) );
+ }
+ }
+
+ m_pImp->GetShell()->EnableSmooth( true );
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( m_pImp->IsAccessible() )
+ m_pImp->FireAccessibleEvents();
+#endif
+
+ SAL_INFO("sw.idle", "SwLayIdle() return");
+
+#ifdef DBG_UTIL
+ if ( m_bIndicator && m_pImp->GetShell()->GetWin() )
+ {
+ // Do not invalidate indicator, this may cause an endless loop. Instead, just repaint it
+ // This should be replaced by an overlay object in the future, anyways. Since it's only for debug
+ // purposes, it is not urgent.
+ m_bIndicator = false; SHOW_IDLE( COL_LIGHTGREEN );
+ }
+#endif
+}
+
+SwLayIdle::~SwLayIdle()
+{
+ m_pImp->m_pIdleAct = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/laycache.cxx b/sw/source/core/layout/laycache.cxx
new file mode 100644
index 000000000..e5b1dc341
--- /dev/null
+++ b/sw/source/core/layout/laycache.cxx
@@ -0,0 +1,1200 @@
+/* -*- 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 <editeng/formatbreakitem.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <tools/stream.hxx>
+#include <doc.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docstat.hxx>
+#include <fmtpdsc.hxx>
+#include <laycache.hxx>
+#include "layhelp.hxx"
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <swtable.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <sectfrm.hxx>
+#include <fmtcntnt.hxx>
+#include <pagedesc.hxx>
+#include <frmtool.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <viewopt.hxx>
+#include <flyfrm.hxx>
+#include <sortedobjs.hxx>
+#include <ndindex.hxx>
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <frameformats.hxx>
+
+#include <limits>
+
+using namespace ::com::sun::star;
+
+SwLayoutCache::SwLayoutCache() : m_nLockCount( 0 ) {}
+
+/*
+ * Reading and writing of the layout cache.
+ * The layout cache is not necessary, but it improves
+ * the performance and reduces the text flow during
+ * the formatting.
+ * The layout cache contains the index of the paragraphs/tables
+ * at the top of every page, so it's possible to create
+ * the right count of pages and to distribute the document content
+ * to this pages before the formatting starts.
+ */
+
+void SwLayoutCache::Read( SvStream &rStream )
+{
+ if( !m_pImpl )
+ {
+ m_pImpl.reset( new SwLayCacheImpl );
+ if( !m_pImpl->Read( rStream ) )
+ {
+ m_pImpl.reset();
+ }
+ }
+}
+
+void SwLayCacheImpl::Insert( sal_uInt16 nType, SwNodeOffset nIndex, sal_Int32 nOffset )
+{
+ m_aType.push_back( nType );
+ mIndices.push_back( nIndex );
+ m_aOffset.push_back( nOffset );
+}
+
+bool SwLayCacheImpl::Read( SvStream& rStream )
+{
+ SwLayCacheIoImpl aIo( rStream, false );
+ if( aIo.GetMajorVersion() > SW_LAYCACHE_IO_VERSION_MAJOR )
+ return false;
+
+ // Due to an evil bug in the layout cache (#102759#), we cannot trust the
+ // sizes of fly frames which have been written using the "old" layout cache.
+ // This flag should indicate that we do not want to trust the width and
+ // height of fly frames
+ m_bUseFlyCache = aIo.GetMinorVersion() >= 1;
+
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
+ aIo.OpenFlagRec();
+ aIo.CloseFlagRec();
+ while( aIo.BytesLeft() && !aIo.HasError() )
+ {
+ sal_uInt32 nIndex(0), nOffset(0);
+
+ switch( aIo.Peek() )
+ {
+ case SW_LAYCACHE_IO_REC_PARA:
+ {
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
+ sal_uInt8 cFlags = aIo.OpenFlagRec();
+ aIo.GetStream().ReadUInt32( nIndex );
+ if( (cFlags & 0x01) != 0 )
+ aIo.GetStream().ReadUInt32( nOffset );
+ else
+ nOffset = COMPLETE_STRING;
+ aIo.CloseFlagRec();
+ Insert( SW_LAYCACHE_IO_REC_PARA, SwNodeOffset(nIndex), static_cast<sal_Int32>(nOffset) );
+ aIo.CloseRec();
+ break;
+ }
+ case SW_LAYCACHE_IO_REC_TABLE:
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
+ aIo.OpenFlagRec();
+ aIo.GetStream().ReadUInt32( nIndex )
+ .ReadUInt32( nOffset );
+ Insert( SW_LAYCACHE_IO_REC_TABLE, SwNodeOffset(nIndex), static_cast<sal_Int32>(nOffset) );
+ aIo.CloseFlagRec();
+ aIo.CloseRec();
+ break;
+ case SW_LAYCACHE_IO_REC_FLY:
+ {
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
+ aIo.OpenFlagRec();
+ aIo.CloseFlagRec();
+ sal_Int32 nX(0), nY(0), nW(0), nH(0);
+ sal_uInt16 nPgNum(0);
+ aIo.GetStream().ReadUInt16( nPgNum ).ReadUInt32( nIndex )
+ .ReadInt32( nX ).ReadInt32( nY ).ReadInt32( nW ).ReadInt32( nH );
+ m_FlyCache.emplace_back( nPgNum, nIndex, nX, nY, nW, nH );
+ aIo.CloseRec();
+ break;
+ }
+ default:
+ aIo.SkipRec();
+ break;
+ }
+ }
+ aIo.CloseRec();
+
+ return !aIo.HasError();
+}
+
+/** writes the index (more precise: the difference between
+ * the index and the first index of the document content)
+ * of the first paragraph/table at the top of every page.
+ * If at the top of a page is the rest of a paragraph/table
+ * from the bottom of the previous page, the character/row
+ * number is stored, too.
+ * The position, size and page number of the text frames
+ * are stored, too
+ */
+void SwLayoutCache::Write( SvStream &rStream, const SwDoc& rDoc )
+{
+ if( !rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ) // the layout itself ..
+ return;
+
+ SwLayCacheIoImpl aIo( rStream, true );
+ // We want to save the relative index, so we need the index
+ // of the first content
+ SwNodeOffset nStartOfContent = rDoc.GetNodes().GetEndOfContent().
+ StartOfSectionNode()->GetIndex();
+ // The first page...
+ SwPageFrame* pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower()));
+
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
+ aIo.OpenFlagRec( 0, 0 );
+ aIo.CloseFlagRec();
+ while( pPage )
+ {
+ if( pPage->GetPrev() )
+ {
+ SwLayoutFrame* pLay = pPage->FindBodyCont();
+ SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr;
+ // We are only interested in paragraph or table frames,
+ // a section frames contains paragraphs/tables.
+ if( pTmp && pTmp->IsSctFrame() )
+ pTmp = static_cast<SwSectionFrame*>(pTmp)->ContainsAny();
+
+ if( pTmp ) // any content
+ {
+ if( pTmp->IsTextFrame() )
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
+ assert(!pFrame->GetMergedPara());
+ SwNodeOffset nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ /* Open Paragraph Record */
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
+ bool bFollow = static_cast<SwTextFrame*>(pTmp)->IsFollow();
+ aIo.OpenFlagRec( bFollow ? 0x01 : 0x00,
+ bFollow ? 8 : 4 );
+ nNdIdx -= nStartOfContent;
+ aIo.GetStream().WriteUInt32( sal_Int32(nNdIdx) );
+ if( bFollow )
+ aIo.GetStream().WriteUInt32( sal_Int32(static_cast<SwTextFrame*>(pTmp)->GetOffset()) );
+ aIo.CloseFlagRec();
+ /* Close Paragraph Record */
+ aIo.CloseRec();
+ }
+ }
+ else if( pTmp->IsTabFrame() )
+ {
+ SwTabFrame* pTab = static_cast<SwTabFrame*>(pTmp);
+ sal_uLong nOfst = COMPLETE_STRING;
+ if( pTab->IsFollow() )
+ {
+ // If the table is a follow, we have to look for the
+ // master and to count all rows to get the row number
+ nOfst = 0;
+ if( pTab->IsFollow() )
+ pTab = pTab->FindMaster( true );
+ while( pTab != pTmp )
+ {
+ SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ assert(pTab && "Table follow without master");
+ }
+ }
+ while (true)
+ {
+ SwNodeOffset nNdIdx =
+ pTab->GetTable()->GetTableNode()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ /* Open Table Record */
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
+ aIo.OpenFlagRec( 0, 8 );
+ nNdIdx -= nStartOfContent;
+ aIo.GetStream().WriteUInt32( sal_Int32(nNdIdx) )
+ .WriteUInt32( nOfst );
+ aIo.CloseFlagRec();
+ /* Close Table Record */
+ aIo.CloseRec();
+ }
+ // If the table has a follow on the next page,
+ // we know already the row number and store this
+ // immediately.
+ if( pTab->GetFollow() )
+ {
+ if( nOfst == sal_uLong(COMPLETE_STRING) )
+ nOfst = 0;
+ do
+ {
+ SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ SwPageFrame *pTabPage = pTab->FindPageFrame();
+ if( pTabPage != pPage )
+ {
+ OSL_ENSURE( pPage->GetPhyPageNum() <
+ pTabPage->GetPhyPageNum(),
+ "Looping Tableframes" );
+ pPage = pTabPage;
+ break;
+ }
+ } while ( pTab->GetFollow() );
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+ if( pPage->GetSortedObjs() )
+ {
+ SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if (SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame())
+ {
+ if( pFly->getFrameArea().Left() != FAR_AWAY &&
+ !pFly->GetAnchorFrame()->FindFooterOrHeader() )
+ {
+ const SwContact *pC =
+ ::GetUserCall(pAnchoredObj->GetDrawObj());
+ if( pC )
+ {
+ sal_uInt32 nOrdNum = pAnchoredObj->GetDrawObj()->GetOrdNum();
+ sal_uInt16 nPageNum = pPage->GetPhyPageNum();
+ /* Open Fly Record */
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
+ aIo.OpenFlagRec( 0, 0 );
+ aIo.CloseFlagRec();
+ const SwRect& rRct = pFly->getFrameArea();
+ sal_Int32 nX = rRct.Left() - pPage->getFrameArea().Left();
+ sal_Int32 nY = rRct.Top() - pPage->getFrameArea().Top();
+ aIo.GetStream().WriteUInt16( nPageNum ).WriteUInt32( nOrdNum )
+ .WriteInt32( nX ).WriteInt32( nY )
+ .WriteInt32( rRct.Width() )
+ .WriteInt32( rRct.Height() );
+ /* Close Fly Record */
+ aIo.CloseRec();
+ }
+ }
+ }
+ }
+ }
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ aIo.CloseRec();
+}
+
+#ifdef DBG_UTIL
+bool SwLayoutCache::CompareLayout( const SwDoc& rDoc ) const
+{
+ if( !m_pImpl )
+ return true;
+ const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pRootFrame )
+ {
+ size_t nIndex = 0;
+ SwNodeOffset nStartOfContent = rDoc.GetNodes().GetEndOfContent().
+ StartOfSectionNode()->GetIndex();
+ const SwPageFrame* pPage = static_cast<const SwPageFrame*>(pRootFrame->Lower());
+ if( pPage )
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ while( pPage )
+ {
+ if( nIndex >= m_pImpl->size() )
+ return false;
+
+ const SwLayoutFrame* pLay = pPage->FindBodyCont();
+ const SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr;
+ if( pTmp && pTmp->IsSctFrame() )
+ pTmp = static_cast<const SwSectionFrame*>(pTmp)->ContainsAny();
+ if( pTmp )
+ {
+ if( pTmp->IsTextFrame() )
+ {
+
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
+ assert(!pFrame->GetMergedPara());
+ SwNodeOffset nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ bool bFollow = static_cast<const SwTextFrame*>(pTmp)->IsFollow();
+ nNdIdx -= nStartOfContent;
+ if( m_pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
+ SW_LAYCACHE_IO_REC_PARA !=
+ m_pImpl->GetBreakType( nIndex ) ||
+ (bFollow
+ ? sal_Int32(static_cast<const SwTextFrame*>(pTmp)->GetOffset())
+ : COMPLETE_STRING) != m_pImpl->GetBreakOfst(nIndex))
+ {
+ return false;
+ }
+ ++nIndex;
+ }
+ }
+ else if( pTmp->IsTabFrame() )
+ {
+ const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pTmp);
+ sal_Int32 nOfst = COMPLETE_STRING;
+ if( pTab->IsFollow() )
+ {
+ nOfst = 0;
+ if( pTab->IsFollow() )
+ pTab = pTab->FindMaster( true );
+ while( pTab != pTmp )
+ {
+ const SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ }
+ }
+ do
+ {
+ SwNodeOffset nNdIdx =
+ pTab->GetTable()->GetTableNode()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ nNdIdx -= nStartOfContent;
+ if( m_pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
+ SW_LAYCACHE_IO_REC_TABLE !=
+ m_pImpl->GetBreakType( nIndex ) ||
+ nOfst != m_pImpl->GetBreakOfst( nIndex ) )
+ {
+ return false;
+ }
+ ++nIndex;
+ }
+ if( pTab->GetFollow() )
+ {
+ if( nOfst == COMPLETE_STRING )
+ nOfst = 0;
+ do
+ {
+ const SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ const SwPageFrame *pTabPage = pTab->FindPageFrame();
+ if( pTabPage != pPage )
+ {
+ pPage = pTabPage;
+ break;
+ }
+ } while ( pTab->GetFollow() );
+ }
+ else
+ break;
+ } while( pTab );
+ }
+ }
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+ }
+ return true;
+}
+#endif
+
+void SwLayoutCache::ClearImpl()
+{
+ if( !IsLocked() )
+ {
+ m_pImpl.reset();
+ }
+}
+
+SwLayoutCache::~SwLayoutCache()
+{
+ OSL_ENSURE( !m_nLockCount, "Deleting a locked SwLayoutCache!?" );
+}
+
+/// helper class to create not nested section frames for nested sections.
+SwActualSection::SwActualSection( SwActualSection *pUp,
+ SwSectionFrame *pSect,
+ SwSectionNode *pNd ) :
+ m_pUpper( pUp ),
+ m_pSectFrame( pSect ),
+ m_pSectNode( pNd )
+{
+ if ( !m_pSectNode )
+ {
+ const SwNodeIndex *pIndex = pSect->GetFormat()->GetContent().GetContentIdx();
+ m_pSectNode = pIndex->GetNode().FindSectionNode();
+ }
+}
+
+namespace {
+
+bool sanityCheckLayoutCache(SwLayCacheImpl const& rCache,
+ SwNodes const& rNodes, SwNodeOffset nNodeIndex)
+{
+ auto const nStartOfContent(rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex());
+ nNodeIndex -= nStartOfContent;
+ auto const nMaxIndex(rNodes.GetEndOfContent().GetIndex() - nStartOfContent);
+ for (size_t nIndex = 0; nIndex < rCache.size(); ++nIndex)
+ {
+ auto const nBreakIndex(rCache.GetBreakIndex(nIndex));
+ if (nBreakIndex < nNodeIndex || nMaxIndex <= nBreakIndex)
+ {
+ SAL_WARN("sw.layout",
+ "invalid node index in layout-cache: " << nBreakIndex);
+ return false;
+ }
+ auto const nBreakType(rCache.GetBreakType(nIndex));
+ switch (nBreakType)
+ {
+ case SW_LAYCACHE_IO_REC_PARA:
+ if (!rNodes[nBreakIndex + nStartOfContent]->IsTextNode())
+ {
+ SAL_WARN("sw.layout",
+ "invalid node of type 'P' in layout-cache");
+ return false;
+ }
+ break;
+ case SW_LAYCACHE_IO_REC_TABLE:
+ if (!rNodes[nBreakIndex + nStartOfContent]->IsTableNode())
+ {
+ SAL_WARN("sw.layout",
+ "invalid node of type 'T' in layout-cache");
+ return false;
+ }
+ break;
+ default:
+ assert(false); // Read shouldn't have inserted that
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+/** helper class, which utilizes the layout cache information
+ * to distribute the document content to the right pages.
+ * It's used by the InsertCnt_(..)-function.
+ * If there's no layout cache, the distribution to the pages is more
+ * a guess, but a guess with statistical background.
+ */
+SwLayHelper::SwLayHelper( SwDoc *pD, SwFrame* &rpF, SwFrame* &rpP, SwPageFrame* &rpPg,
+ SwLayoutFrame* &rpL, std::unique_ptr<SwActualSection> &rpA,
+ SwNodeOffset nNodeIndex, bool bCache )
+ : mrpFrame( rpF )
+ , mrpPrv( rpP )
+ , mrpPage( rpPg )
+ , mrpLay( rpL )
+ , mrpActualSection( rpA )
+ , mbBreakAfter(false)
+ , mpDoc(pD)
+ , mnMaxParaPerPage( 25 )
+ , mnParagraphCnt( bCache ? 0 : USHRT_MAX )
+ , mnFlyIdx( 0 )
+ , mbFirst( bCache )
+{
+ mpImpl = mpDoc->GetLayoutCache() ? mpDoc->GetLayoutCache()->LockImpl() : nullptr;
+ if( mpImpl )
+ {
+ SwNodes const& rNodes(mpDoc->GetNodes());
+ if (sanityCheckLayoutCache(*mpImpl, rNodes, nNodeIndex))
+ {
+ mnIndex = 0;
+ mnStartOfContent = rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex();
+ mnMaxParaPerPage = 1000;
+ }
+ else
+ {
+ mpDoc->GetLayoutCache()->UnlockImpl();
+ mpImpl = nullptr;
+ mnIndex = std::numeric_limits<size_t>::max();
+ mnStartOfContent = SwNodeOffset(USHRT_MAX);
+ }
+ }
+ else
+ {
+ mnIndex = std::numeric_limits<size_t>::max();
+ mnStartOfContent = NODE_OFFSET_MAX;
+ }
+}
+
+SwLayHelper::~SwLayHelper()
+{
+ if( mpImpl )
+ {
+ OSL_ENSURE( mpDoc && mpDoc->GetLayoutCache(), "Missing layoutcache" );
+ mpDoc->GetLayoutCache()->UnlockImpl();
+ }
+}
+
+/** Does NOT really calculate the page count,
+ * it returns the page count value from the layout cache, if available,
+ * otherwise it estimates the page count.
+ */
+sal_uLong SwLayHelper::CalcPageCount()
+{
+ sal_uLong nPgCount;
+ SwLayCacheImpl *pCache = mpDoc->GetLayoutCache() ?
+ mpDoc->GetLayoutCache()->LockImpl() : nullptr;
+ if( pCache )
+ {
+ nPgCount = pCache->size() + 1;
+ mpDoc->GetLayoutCache()->UnlockImpl();
+ }
+ else
+ {
+ nPgCount = mpDoc->getIDocumentStatistics().GetDocStat().nPage;
+ if ( nPgCount <= 10 ) // no page insertion for less than 10 pages
+ nPgCount = 0;
+ sal_Int32 nNdCount = mpDoc->getIDocumentStatistics().GetDocStat().nPara;
+ if ( nNdCount <= 1 )
+ {
+ //Estimates the number of paragraphs.
+ SwNodeOffset nTmp = mpDoc->GetNodes().GetEndOfContent().GetIndex() -
+ mpDoc->GetNodes().GetEndOfExtras().GetIndex();
+ //Tables have a little overhead...
+ nTmp -= SwNodeOffset(mpDoc->GetTableFrameFormats()->size() * 25);
+ //Fly frames, too ..
+ nTmp -= (mpDoc->GetNodes().GetEndOfAutotext().GetIndex() -
+ mpDoc->GetNodes().GetEndOfInserts().GetIndex()) / SwNodeOffset(3 * 5);
+ if ( nTmp > SwNodeOffset(0) )
+ nNdCount = sal_Int32(nTmp);
+ }
+ if ( nNdCount > 100 ) // no estimation below this value
+ {
+ if ( nPgCount > 0 )
+ { // tdf#129529 avoid 0...
+ mnMaxParaPerPage = std::max<sal_uLong>(3, nNdCount / nPgCount);
+ }
+ else
+ {
+ mnMaxParaPerPage = std::max( sal_uLong(20),
+ sal_uLong(20 + nNdCount / 1000 * 3) );
+ const sal_uLong nMax = 53;
+ mnMaxParaPerPage = std::min( mnMaxParaPerPage, nMax );
+ nPgCount = nNdCount / mnMaxParaPerPage;
+ }
+ if ( nNdCount < 1000 )
+ nPgCount = 0;// no progress bar for small documents
+ SwViewShell *pSh = nullptr;
+ if( mrpLay && mrpLay->getRootFrame() )
+ pSh = mrpLay->getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ mnMaxParaPerPage *= 6;
+ }
+ }
+ return nPgCount;
+}
+
+/**
+ * inserts a page and return true, if
+ * - the break after flag is set
+ * - the actual content wants a break before
+ * - the maximum count of paragraph/rows is reached
+ *
+ * The break after flag is set, if the actual content
+ * wants a break after.
+ */
+bool SwLayHelper::CheckInsertPage()
+{
+ bool bEnd = nullptr == mrpPage->GetNext();
+ const SvxFormatBreakItem& rBrk = mrpFrame->GetBreakItem();
+ const SwFormatPageDesc& rDesc = mrpFrame->GetPageDescItem();
+ // #118195# Do not evaluate page description if frame
+ // is a follow frame!
+ const SwPageDesc* pDesc = mrpFrame->IsFlowFrame() &&
+ SwFlowFrame::CastFlowFrame( mrpFrame )->IsFollow() ?
+ nullptr :
+ rDesc.GetPageDesc();
+
+ bool bBrk = mnParagraphCnt > mnMaxParaPerPage || mbBreakAfter;
+ mbBreakAfter = rBrk.GetBreak() == SvxBreak::PageAfter ||
+ rBrk.GetBreak() == SvxBreak::PageBoth;
+ if ( !bBrk )
+ bBrk = rBrk.GetBreak() == SvxBreak::PageBefore ||
+ rBrk.GetBreak() == SvxBreak::PageBoth;
+
+ if ( bBrk || pDesc )
+ {
+ ::std::optional<sal_uInt16> oPgNum;
+ if ( !pDesc )
+ {
+ pDesc = mrpPage->GetPageDesc()->GetFollow();
+ }
+ else
+ {
+ oPgNum = rDesc.GetNumOffset();
+ if ( oPgNum )
+ static_cast<SwRootFrame*>(mrpPage->GetUpper())->SetVirtPageNum(true);
+ }
+ bool bNextPageRight = !mrpPage->OnRightPage();
+ bool bInsertEmpty = false;
+ assert(mrpPage->GetUpper()->GetLower());
+ if (oPgNum && bNextPageRight != IsRightPageByNumber(
+ *static_cast<SwRootFrame*>(mrpPage->GetUpper()), *oPgNum))
+ {
+ bNextPageRight = !bNextPageRight;
+ bInsertEmpty = true;
+ }
+ // If the page style is changing, we'll have a first page.
+ bool bNextPageFirst = pDesc != mrpPage->GetPageDesc();
+ ::InsertNewPage( const_cast<SwPageDesc&>(*pDesc), mrpPage->GetUpper(),
+ bNextPageRight, bNextPageFirst, bInsertEmpty, false, mrpPage->GetNext());
+ if ( bEnd )
+ {
+ OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
+ do
+ { mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
+ } while ( mrpPage->GetNext() );
+ }
+ else
+ {
+ OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
+ mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
+ if ( mrpPage->IsEmptyPage() )
+ {
+ OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
+ mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
+ }
+ }
+ mrpLay = mrpPage->FindBodyCont();
+ while( mrpLay->Lower() )
+ mrpLay = static_cast<SwLayoutFrame*>(mrpLay->Lower());
+ return true;
+ }
+ return false;
+}
+
+/** entry point for the InsertCnt_-function.
+ * The document content index is checked either it is
+ * in the layout cache either it's time to insert a page
+ * cause the maximal estimation of content per page is reached.
+ * A really big table or long paragraph may contains more than
+ * one page, in this case the needed count of pages will inserted.
+ */
+bool SwLayHelper::CheckInsert( SwNodeOffset nNodeIndex )
+{
+ bool bRet = false;
+ bool bLongTab = false;
+ sal_uLong nMaxRowPerPage( 0 );
+ nNodeIndex -= mnStartOfContent;
+ sal_uInt16 nRows( 0 );
+ if( mrpFrame->IsTabFrame() )
+ {
+ //Inside a table counts every row as a paragraph
+ SwFrame *pLow = static_cast<SwTabFrame*>(mrpFrame)->Lower();
+ nRows = 0;
+ do
+ {
+ ++nRows;
+ pLow = pLow->GetNext();
+ } while ( pLow );
+ mnParagraphCnt += nRows;
+ if( !mpImpl && mnParagraphCnt > mnMaxParaPerPage + 10 )
+ {
+ // OD 09.04.2003 #108698# - improve heuristics:
+ // Assume that a table, which has more than three times the quantity
+ // of maximal paragraphs per page rows, consists of rows, which have
+ // the height of a normal paragraph. Thus, allow as much rows per page
+ // as much paragraphs are allowed.
+ if ( nRows > ( 3*mnMaxParaPerPage ) )
+ {
+ nMaxRowPerPage = mnMaxParaPerPage;
+ }
+ else
+ {
+ SwFrame *pTmp = static_cast<SwTabFrame*>(mrpFrame)->Lower();
+ if( pTmp->GetNext() )
+ pTmp = pTmp->GetNext();
+ pTmp = static_cast<SwRowFrame*>(pTmp)->Lower();
+ sal_uInt16 nCnt = 0;
+ do
+ {
+ ++nCnt;
+ pTmp = pTmp->GetNext();
+ } while( pTmp );
+ nMaxRowPerPage = std::max( sal_uLong(2), mnMaxParaPerPage / nCnt );
+ }
+ bLongTab = true;
+ }
+ }
+ else
+ ++mnParagraphCnt;
+ if( mbFirst && mpImpl && mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex &&
+ ( mpImpl->GetBreakOfst( mnIndex ) < COMPLETE_STRING ||
+ ( ++mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) ) )
+ mbFirst = false;
+ // OD 09.04.2003 #108698# - always split a big tables.
+ if ( !mbFirst ||
+ ( mrpFrame->IsTabFrame() && bLongTab )
+ )
+ {
+ sal_Int32 nRowCount = 0;
+ do
+ {
+ if( mpImpl || bLongTab )
+ {
+ sal_Int32 nOfst = COMPLETE_STRING;
+ sal_uInt16 nType = SW_LAYCACHE_IO_REC_PAGES;
+ if( bLongTab )
+ {
+ mbBreakAfter = true;
+ nOfst = static_cast<sal_Int32>(nRowCount + nMaxRowPerPage);
+ }
+ else
+ {
+ while( mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex(mnIndex) < nNodeIndex)
+ ++mnIndex;
+ if( mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex(mnIndex) == nNodeIndex )
+ {
+ nType = mpImpl->GetBreakType( mnIndex );
+ nOfst = mpImpl->GetBreakOfst( mnIndex++ );
+ mbBreakAfter = true;
+ }
+ }
+
+ if( nOfst < COMPLETE_STRING )
+ {
+ bool bSplit = false;
+ sal_uInt16 nRepeat( 0 );
+ if( !bLongTab && mrpFrame->IsTextFrame() &&
+ SW_LAYCACHE_IO_REC_PARA == nType &&
+ nOfst < static_cast<SwTextFrame*>(mrpFrame)->GetText().getLength())
+ bSplit = true;
+ else if( mrpFrame->IsTabFrame() && nRowCount < nOfst &&
+ ( bLongTab || SW_LAYCACHE_IO_REC_TABLE == nType ) )
+ {
+ nRepeat = static_cast<SwTabFrame*>(mrpFrame)->
+ GetTable()->GetRowsToRepeat();
+ bSplit = nOfst < nRows && nRowCount + nRepeat < nOfst;
+ bLongTab = bLongTab && bSplit;
+ }
+ if( bSplit )
+ {
+ mrpFrame->InsertBehind( mrpLay, mrpPrv );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpFrame);
+ aFrm.Pos() = mrpLay->getFrameArea().Pos();
+ aFrm.Pos().AdjustY(1 );
+ }
+
+ mrpPrv = mrpFrame;
+ if( mrpFrame->IsTabFrame() )
+ {
+ SwTabFrame* pTab = static_cast<SwTabFrame*>(mrpFrame);
+ // #i33629#, #i29955#
+ ::RegistFlys( pTab->FindPageFrame(), pTab );
+ SwFrame *pRow = pTab->Lower();
+ SwTabFrame *pFoll = new SwTabFrame( *pTab );
+
+ SwFrame *pPrv;
+ if( nRepeat > 0 )
+ {
+ sw::FlyCreationSuppressor aSuppressor;
+ // Insert new headlines:
+ sal_uInt16 nRowIdx = 0;
+ SwRowFrame* pHeadline = nullptr;
+ while( nRowIdx < nRepeat )
+ {
+ OSL_ENSURE( pTab->GetTable()->GetTabLines()[ nRowIdx ], "Table without rows?" );
+ pHeadline =
+ new SwRowFrame( *pTab->GetTable()->GetTabLines()[ nRowIdx ], pTab );
+ pHeadline->SetRepeatedHeadline( true );
+ pHeadline->InsertBefore( pFoll, nullptr );
+ pHeadline->RegistFlys();
+
+ ++nRowIdx;
+ }
+ pPrv = pHeadline;
+ nRows = nRows + nRepeat;
+ }
+ else
+ pPrv = nullptr;
+ while( pRow && nRowCount < nOfst )
+ {
+ pRow = pRow->GetNext();
+ ++nRowCount;
+ }
+ while ( pRow )
+ {
+ SwFrame* pNxt = pRow->GetNext();
+ pRow->RemoveFromLayout();
+ pRow->InsertBehind( pFoll, pPrv );
+ pPrv = pRow;
+ pRow = pNxt;
+ }
+ mrpFrame = pFoll;
+ }
+ else
+ {
+ SwTextFrame *const pNew = static_cast<SwTextFrame*>(
+ static_cast<SwTextFrame*>(mrpFrame)
+ ->GetTextNodeFirst()->MakeFrame(mrpFrame));
+ pNew->ManipOfst( TextFrameIndex(nOfst) );
+ pNew->SetFollow( static_cast<SwTextFrame*>(mrpFrame)->GetFollow() );
+ static_cast<SwTextFrame*>(mrpFrame)->SetFollow( pNew );
+ mrpFrame = pNew;
+ }
+ }
+ }
+ }
+
+ SwPageFrame* pLastPage = mrpPage;
+ if( CheckInsertPage() )
+ {
+ CheckFlyCache_( pLastPage );
+ if( mrpPrv && mrpPrv->IsTextFrame() && !mrpPrv->isFrameAreaSizeValid() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpPrv);
+ aFrm.Height( mrpPrv->GetUpper()->getFramePrintArea().Height() );
+ }
+
+ bRet = true;
+ mrpPrv = nullptr;
+ mnParagraphCnt = 0;
+
+ if ( mrpActualSection )
+ {
+ //Did the SectionFrame even have a content? If not, we can
+ //directly put it somewhere else
+ SwSectionFrame *pSct;
+ bool bInit = false;
+ if ( !mrpActualSection->GetSectionFrame()->ContainsContent())
+ {
+ pSct = mrpActualSection->GetSectionFrame();
+ pSct->RemoveFromLayout();
+ }
+ else
+ {
+ pSct = new SwSectionFrame(
+ *mrpActualSection->GetSectionFrame(), false );
+ mrpActualSection->GetSectionFrame()->SimpleFormat();
+ bInit = true;
+ }
+ mrpActualSection->SetSectionFrame( pSct );
+ pSct->InsertBehind( mrpLay, nullptr );
+
+ if( bInit )
+ {
+ pSct->Init();
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pSct);
+ aFrm.Pos() = mrpLay->getFrameArea().Pos();
+ aFrm.Pos().AdjustY(1 ); //because of the notifications
+ }
+
+ mrpLay = pSct;
+ if ( mrpLay->Lower() && mrpLay->Lower()->IsLayoutFrame() )
+ mrpLay = mrpLay->GetNextLayoutLeaf();
+ }
+ }
+ } while( bLongTab || ( mpImpl && mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) );
+ }
+ mbFirst = false;
+ return bRet;
+}
+
+namespace {
+
+struct SdrObjectCompare
+{
+ bool operator()( const SdrObject* pF1, const SdrObject* pF2 ) const
+ {
+ return pF1->GetOrdNum() < pF2->GetOrdNum();
+ }
+};
+
+struct FlyCacheCompare
+{
+ bool operator()( const SwFlyCache* pC1, const SwFlyCache* pC2 ) const
+ {
+ return pC1->nOrdNum < pC2->nOrdNum;
+ }
+};
+
+}
+
+/**
+ * If a new page is inserted, the last page is analysed.
+ * If there are text frames with default position, the fly cache
+ * is checked, if these frames are stored in the cache.
+ */
+void SwLayHelper::CheckFlyCache_( SwPageFrame* pPage )
+{
+ if( !mpImpl || !pPage )
+ return;
+ const size_t nFlyCount = mpImpl->GetFlyCount();
+ // Any text frames at the page, fly cache available?
+ if( !(pPage->GetSortedObjs() && mnFlyIdx < nFlyCount) )
+ return;
+
+ SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ sal_uInt16 nPgNum = pPage->GetPhyPageNum();
+
+ // NOTE: Here we do not use the absolute ordnums but
+ // relative ordnums for the objects on this page.
+
+ // skip fly frames from pages before the current page
+ while( mnFlyIdx < nFlyCount &&
+ mpImpl->GetFlyCache(mnFlyIdx).nPageNum < nPgNum )
+ ++mnFlyIdx;
+
+ // sort cached objects on this page by ordnum
+ o3tl::sorted_vector< const SwFlyCache*, FlyCacheCompare > aFlyCacheSet;
+ size_t nIdx = mnFlyIdx;
+
+ SwFlyCache* pFlyC;
+ while( nIdx < nFlyCount &&
+ ( pFlyC = &mpImpl->GetFlyCache( nIdx ) )->nPageNum == nPgNum )
+ {
+ aFlyCacheSet.insert( pFlyC );
+ ++nIdx;
+ }
+
+ // sort objects on this page by ordnum
+ o3tl::sorted_vector< const SdrObject*, SdrObjectCompare > aFlySet;
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if (SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame()) // a text frame?
+ {
+ if( pFly->GetAnchorFrame() &&
+ !pFly->GetAnchorFrame()->FindFooterOrHeader() )
+ {
+ const SwContact *pC = ::GetUserCall( pAnchoredObj->GetDrawObj() );
+ if( pC )
+ {
+ aFlySet.insert( pAnchoredObj->GetDrawObj() );
+ }
+ }
+ }
+ }
+
+ if ( aFlyCacheSet.size() != aFlySet.size() )
+ return;
+
+ auto aFlySetIt = aFlySet.begin();
+
+ for ( const SwFlyCache* pFlyCache : aFlyCacheSet )
+ {
+ SwFlyFrame* pFly = const_cast<SwVirtFlyDrawObj*>(static_cast<const SwVirtFlyDrawObj*>(*aFlySetIt))->GetFlyFrame();
+
+ if ( pFly->getFrameArea().Left() == FAR_AWAY )
+ {
+ // we get the stored information
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
+ aFrm.Pos().setX( pFlyCache->Left() + pPage->getFrameArea().Left() );
+ aFrm.Pos().setY( pFlyCache->Top() + pPage->getFrameArea().Top() );
+
+ if ( mpImpl->IsUseFlyCache() )
+ {
+ aFrm.Width( pFlyCache->Width() );
+ aFrm.Height( pFlyCache->Height() );
+ }
+ }
+
+ ++aFlySetIt;
+ }
+}
+
+SwLayCacheIoImpl::SwLayCacheIoImpl( SvStream& rStrm, bool bWrtMd ) :
+ m_pStream( &rStrm ),
+ m_nFlagRecEnd ( 0 ),
+ m_nMajorVersion(SW_LAYCACHE_IO_VERSION_MAJOR),
+ m_nMinorVersion(SW_LAYCACHE_IO_VERSION_MINOR),
+ m_bWriteMode( bWrtMd ),
+ m_bError( false )
+{
+ if( m_bWriteMode )
+ m_pStream->WriteUInt16( m_nMajorVersion )
+ .WriteUInt16( m_nMinorVersion );
+
+ else
+ m_pStream->ReadUInt16( m_nMajorVersion )
+ .ReadUInt16( m_nMinorVersion );
+}
+
+void SwLayCacheIoImpl::OpenRec( sal_uInt8 cType )
+{
+ sal_uInt32 nPos = m_pStream->Tell();
+ if( m_bWriteMode )
+ {
+ m_aRecords.emplace_back(cType, nPos );
+ m_pStream->WriteUInt32( 0 );
+ }
+ else
+ {
+ sal_uInt32 nVal(0);
+ m_pStream->ReadUInt32( nVal );
+ sal_uInt8 cRecTyp = static_cast<sal_uInt8>(nVal);
+ if (!nVal || cRecTyp != cType || !m_pStream->good())
+ {
+ OSL_ENSURE( nVal, "OpenRec: Record-Header is 0" );
+ OSL_ENSURE( cRecTyp == cType, "OpenRec: Wrong Record Type" );
+ m_aRecords.emplace_back(0, m_pStream->Tell() );
+ m_bError = true;
+ }
+ else
+ {
+ sal_uInt32 nSize = nVal >> 8;
+ m_aRecords.emplace_back(cRecTyp, nPos+nSize );
+ }
+ }
+}
+
+// Close record
+void SwLayCacheIoImpl::CloseRec()
+{
+ bool bRes = true;
+ OSL_ENSURE( !m_aRecords.empty(), "CloseRec: no levels" );
+ if( !m_aRecords.empty() )
+ {
+ sal_uInt32 nPos = m_pStream->Tell();
+ if( m_bWriteMode )
+ {
+ sal_uInt32 nBgn = m_aRecords.back().size;
+ m_pStream->Seek( nBgn );
+ sal_uInt32 nSize = nPos - nBgn;
+ sal_uInt32 nVal = ( nSize << 8 ) | m_aRecords.back().type;
+ m_pStream->WriteUInt32( nVal );
+ m_pStream->Seek( nPos );
+ if( m_pStream->GetError() != ERRCODE_NONE )
+ bRes = false;
+ }
+ else
+ {
+ sal_uInt32 n = m_aRecords.back().size;
+ OSL_ENSURE( n >= nPos, "CloseRec: too much data read" );
+ if( n != nPos )
+ {
+ m_pStream->Seek( n );
+ if( n < nPos )
+ bRes = false;
+ }
+ if( m_pStream->GetErrorCode() != ERRCODE_NONE )
+ bRes = false;
+ }
+ m_aRecords.pop_back();
+ }
+
+ if( !bRes )
+ m_bError = true;
+}
+
+sal_uInt32 SwLayCacheIoImpl::BytesLeft()
+{
+ sal_uInt32 n = 0;
+ if( !m_bError && !m_aRecords.empty() )
+ {
+ sal_uInt32 nEndPos = m_aRecords.back().size;
+ sal_uInt32 nPos = m_pStream->Tell();
+ if( nEndPos > nPos )
+ n = nEndPos - nPos;
+ }
+ return n;
+}
+
+sal_uInt8 SwLayCacheIoImpl::Peek()
+{
+ sal_uInt8 c(0);
+ if( !m_bError )
+ {
+ sal_uInt32 nPos = m_pStream->Tell();
+ m_pStream->ReadUChar( c );
+ m_pStream->Seek( nPos );
+ if( m_pStream->GetErrorCode() != ERRCODE_NONE )
+ {
+ c = 0;
+ m_bError = true;
+ }
+ }
+ return c;
+}
+
+void SwLayCacheIoImpl::SkipRec()
+{
+ sal_uInt8 c = Peek();
+ OpenRec( c );
+ m_pStream->Seek( m_aRecords.back().size );
+ CloseRec();
+}
+
+sal_uInt8 SwLayCacheIoImpl::OpenFlagRec()
+{
+ OSL_ENSURE( !m_bWriteMode, "OpenFlagRec illegal in write mode" );
+ sal_uInt8 cFlags(0);
+ m_pStream->ReadUChar( cFlags );
+ m_nFlagRecEnd = m_pStream->Tell() + ( cFlags & 0x0F );
+ return (cFlags >> 4);
+}
+
+void SwLayCacheIoImpl::OpenFlagRec( sal_uInt8 nFlags, sal_uInt8 nLen )
+{
+ OSL_ENSURE( m_bWriteMode, "OpenFlagRec illegal in read mode" );
+ OSL_ENSURE( (nFlags & 0xF0) == 0, "illegal flags set" );
+ OSL_ENSURE( nLen < 16, "wrong flag record length" );
+ sal_uInt8 cFlags = (nFlags << 4) + nLen;
+ m_pStream->WriteUChar( cFlags );
+ m_nFlagRecEnd = m_pStream->Tell() + nLen;
+}
+
+void SwLayCacheIoImpl::CloseFlagRec()
+{
+ if( m_bWriteMode )
+ {
+ OSL_ENSURE( m_pStream->Tell() == m_nFlagRecEnd, "Wrong amount of data written" );
+ }
+ else
+ {
+ OSL_ENSURE( m_pStream->Tell() <= m_nFlagRecEnd, "Too many data read" );
+ if( m_pStream->Tell() != m_nFlagRecEnd )
+ m_pStream->Seek( m_nFlagRecEnd );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/layhelp.hxx b/sw/source/core/layout/layhelp.hxx
new file mode 100644
index 000000000..17c5cc324
--- /dev/null
+++ b/sw/source/core/layout/layhelp.hxx
@@ -0,0 +1,224 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_LAYHELP_HXX
+#define INCLUDED_SW_SOURCE_CORE_LAYOUT_LAYHELP_HXX
+
+#include <swrect.hxx>
+#include <nodeoffset.hxx>
+
+#include <tools/solar.h>
+
+#include <memory>
+#include <vector>
+#include <deque>
+
+class SwDoc;
+class SwFrame;
+class SwLayoutFrame;
+class SwPageFrame;
+class SwSectionFrame;
+class SwSectionNode;
+class SvStream;
+
+/*
+ * Contains the page break information and the text frame positions
+ * of the document (after loading)
+ * and is used inside the constructor of the layout rootframe to
+ * insert content and text frames at the right pages.
+ * For every page of the main text (body content, no footnotes, text frames etc.)
+ * we have the nodeindex of the first content at the page,
+ * the type of content ( table or paragraph )
+ * and if it's not the first part of the table/paragraph,
+ * the row/character-offset inside the table/paragraph.
+ * The text frame positions are stored in the SwPageFlyCache array.
+ */
+
+class SwFlyCache;
+typedef std::vector<SwFlyCache> SwPageFlyCache;
+
+class SwLayCacheImpl
+{
+ std::vector<SwNodeOffset> mIndices;
+ /// either a textframe character offset, or a row index inside a table
+ std::deque<sal_Int32> m_aOffset;
+ std::vector<sal_uInt16> m_aType;
+ SwPageFlyCache m_FlyCache;
+ bool m_bUseFlyCache;
+ void Insert( sal_uInt16 nType, SwNodeOffset nIndex, sal_Int32 nOffset );
+
+public:
+ inline SwLayCacheImpl();
+
+ size_t size() const { return mIndices.size(); }
+
+ bool Read( SvStream& rStream );
+
+ SwNodeOffset GetBreakIndex( size_t nIdx ) const { return mIndices[ nIdx ]; }
+ sal_Int32 GetBreakOfst( size_t nIdx ) const { return m_aOffset[ nIdx ]; }
+ sal_uInt16 GetBreakType( size_t nIdx ) const { return m_aType[ nIdx ]; }
+
+ inline size_t GetFlyCount() const;
+ inline SwFlyCache& GetFlyCache( size_t nIdx );
+
+ bool IsUseFlyCache() const { return m_bUseFlyCache; }
+};
+
+// Helps to create the sectionframes during the InsertCnt_-function
+// by controlling nested sections.
+class SwActualSection
+{
+ SwActualSection *m_pUpper;
+ SwSectionFrame *m_pSectFrame;
+ SwSectionNode *m_pSectNode;
+public:
+ SwActualSection( SwActualSection *pUpper,
+ SwSectionFrame *pSect,
+ SwSectionNode *pNd );
+
+ SwSectionFrame *GetSectionFrame() { return m_pSectFrame; }
+ void SetSectionFrame( SwSectionFrame *p ) { m_pSectFrame = p; }
+ SwSectionNode *GetSectionNode() { return m_pSectNode;}
+ void SetUpper(SwActualSection *p) { m_pUpper = p; }
+ SwActualSection *GetUpper() { return m_pUpper; }
+};
+
+/// Helps during the InsertCnt_ function to create new pages.
+/// If there's a layout cache available, this information is used.
+class SwLayHelper
+{
+ SwFrame* &mrpFrame;
+ SwFrame* &mrpPrv;
+ SwPageFrame* &mrpPage;
+ SwLayoutFrame* &mrpLay;
+ std::unique_ptr<SwActualSection> &mrpActualSection;
+ bool mbBreakAfter;
+ SwDoc* mpDoc;
+ SwLayCacheImpl* mpImpl;
+ sal_uLong mnMaxParaPerPage;
+ sal_uLong mnParagraphCnt;
+ SwNodeOffset mnStartOfContent;
+ size_t mnIndex; ///< the index in the page break array
+ size_t mnFlyIdx; ///< the index in the fly cache array
+ bool mbFirst : 1;
+ void CheckFlyCache_( SwPageFrame* pPage );
+public:
+ SwLayHelper( SwDoc *pD, SwFrame* &rpF, SwFrame* &rpP, SwPageFrame* &rpPg,
+ SwLayoutFrame* &rpL, std::unique_ptr<SwActualSection> &rpA,
+ SwNodeOffset nNodeIndex, bool bCache );
+ ~SwLayHelper();
+ sal_uLong CalcPageCount();
+ bool CheckInsert( SwNodeOffset nNodeIndex );
+
+ bool CheckInsertPage();
+
+ /// Look for fresh text frames at this (new) page and set them to the right
+ /// position, if they are in the fly cache.
+ void CheckFlyCache( SwPageFrame* pPage )
+ { if( mpImpl && mnFlyIdx < mpImpl->GetFlyCount() ) CheckFlyCache_( pPage ); }
+};
+
+// Contains the data structures that are required to read and write a layout cache.
+#define SW_LAYCACHE_IO_REC_PAGES 'p'
+#define SW_LAYCACHE_IO_REC_PARA 'P'
+#define SW_LAYCACHE_IO_REC_TABLE 'T'
+#define SW_LAYCACHE_IO_REC_FLY 'F'
+
+#define SW_LAYCACHE_IO_VERSION_MAJOR 1
+#define SW_LAYCACHE_IO_VERSION_MINOR 1
+
+class SwLayCacheIoImpl
+{
+private:
+ struct RecTypeSize {
+ sal_uInt8 type;
+ sal_uLong size;
+ RecTypeSize(sal_uInt8 typ, sal_uLong siz) : type(typ), size(siz) {}
+ };
+ std::vector<RecTypeSize> m_aRecords;
+
+ SvStream *m_pStream;
+
+ sal_uLong m_nFlagRecEnd;
+
+ sal_uInt16 m_nMajorVersion;
+ sal_uInt16 m_nMinorVersion;
+
+ bool m_bWriteMode : 1;
+ bool m_bError : 1;
+
+public:
+ SwLayCacheIoImpl( SvStream& rStrm, bool bWrtMd );
+
+ /// Get input or output stream
+ SvStream& GetStream() const { return *m_pStream; }
+
+ /// Open a record of type "nType"
+ void OpenRec( sal_uInt8 nType );
+
+ /// Close a record. This skips any unread data that
+ /// remains in the record.
+ void CloseRec();
+
+ /// Return the number of bytes contained in the current record that
+ /// haven't been read by now.
+ sal_uInt32 BytesLeft();
+
+ /// Return the current record's type
+ sal_uInt8 Peek();
+
+ /// Skip the current record
+ void SkipRec();
+
+ /// Open a flag record for reading. The uppermost four bits are flags,
+ /// while the lowermost are the flag record's size. Flag records cannot
+ /// be nested.
+ sal_uInt8 OpenFlagRec();
+
+ /// Open flag record for writing;
+ void OpenFlagRec( sal_uInt8 nFlags, sal_uInt8 nLen );
+
+ /// Close a flag record. Any bytes left are skipped.
+ void CloseFlagRec();
+
+ bool HasError() const { return m_bError; }
+
+ sal_uInt16 GetMajorVersion() const { return m_nMajorVersion; }
+ sal_uInt16 GetMinorVersion() const { return m_nMinorVersion; }
+};
+
+// Stored information about text frames:
+class SwFlyCache : public SwRect // position and size
+{
+public:
+ sal_uLong nOrdNum; ///< Id to recognize text frames
+ sal_uInt16 nPageNum; ///< page number
+ SwFlyCache( sal_uInt16 nP, sal_uLong nO, tools::Long nXL, tools::Long nYL, tools::Long nWL, tools::Long nHL ) :
+ SwRect( nXL, nYL, nWL, nHL ), nOrdNum( nO ), nPageNum( nP ){}
+};
+
+SwLayCacheImpl::SwLayCacheImpl() : m_bUseFlyCache(false) {}
+
+size_t SwLayCacheImpl::GetFlyCount() const { return m_FlyCache.size(); }
+
+SwFlyCache& SwLayCacheImpl::GetFlyCache( size_t nIdx ) { return m_FlyCache[ nIdx ]; }
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/layouter.cxx b/sw/source/core/layout/layouter.cxx
new file mode 100644
index 000000000..57f2ee0ae
--- /dev/null
+++ b/sw/source/core/layout/layouter.cxx
@@ -0,0 +1,483 @@
+/* -*- 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 <memory>
+#include <layouter.hxx>
+#include <doc.hxx>
+#include <sectfrm.hxx>
+#include <pagefrm.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <IDocumentLayoutAccess.hxx>
+
+#include <movedfwdfrmsbyobjpos.hxx>
+#include "objstmpconsiderwrapinfl.hxx"
+#include <osl/diagnose.h>
+
+#define LOOP_DETECT 250
+
+class SwLooping
+{
+ sal_uInt16 mnMinPage;
+ sal_uInt16 mnMaxPage;
+ sal_uInt16 mnCount;
+ sal_uInt16 mnLoopControlStage;
+public:
+ explicit SwLooping( SwPageFrame const * pPage );
+ void Control( SwPageFrame* pPage );
+ void Drastic( SwFrame* pFrame );
+ bool IsLoopingLouieLight() const { return mnCount > LOOP_DETECT - 30; };
+};
+
+class SwEndnoter
+{
+ SwLayouter* m_pMaster;
+ SwSectionFrame* m_pSect;
+ std::unique_ptr<SwFootnoteFrames> m_pEndArr;
+public:
+ explicit SwEndnoter( SwLayouter* pLay )
+ : m_pMaster( pLay ), m_pSect( nullptr ) {}
+ void CollectEndnotes( SwSectionFrame* pSct );
+ void CollectEndnote( SwFootnoteFrame* pFootnote );
+ const SwSectionFrame* GetSect() const { return m_pSect; }
+ void InsertEndnotes();
+ bool HasEndnotes() const { return m_pEndArr && !m_pEndArr->empty(); }
+};
+
+void SwEndnoter::CollectEndnotes( SwSectionFrame* pSct )
+{
+ OSL_ENSURE( pSct, "CollectEndnotes: Which section?" );
+ if( !m_pSect )
+ m_pSect = pSct;
+ else if( pSct != m_pSect )
+ return;
+ m_pSect->CollectEndnotes( m_pMaster );
+}
+
+void SwEndnoter::CollectEndnote( SwFootnoteFrame* pFootnote )
+{
+ if( m_pEndArr && m_pEndArr->end() != std::find( m_pEndArr->begin(), m_pEndArr->end(), pFootnote ) )
+ return;
+
+ if( pFootnote->GetUpper() )
+ {
+ // pFootnote is the master, he incorporates its follows
+ SwFootnoteFrame *pNxt = pFootnote->GetFollow();
+ while ( pNxt )
+ {
+ SwFrame *pCnt = pNxt->ContainsAny();
+ if ( pCnt )
+ {
+ do
+ { SwFrame *pNxtCnt = pCnt->GetNext();
+ pCnt->Cut();
+ pCnt->Paste( pFootnote );
+ pCnt = pNxtCnt;
+ } while ( pCnt );
+ }
+ else
+ {
+ OSL_ENSURE( pNxt->Lower() && pNxt->Lower()->IsSctFrame(),
+ "Endnote without content?" );
+ pNxt->Cut();
+ SwFrame::DestroyFrame(pNxt);
+ }
+ pNxt = pFootnote->GetFollow();
+ }
+ if( pFootnote->GetMaster() )
+ return;
+ pFootnote->Cut();
+ }
+ else if( m_pEndArr )
+ {
+ for (SwFootnoteFrame* pEndFootnote : *m_pEndArr)
+ {
+ if( pEndFootnote->GetAttr() == pFootnote->GetAttr() )
+ {
+ SwFrame::DestroyFrame(pFootnote);
+ return;
+ }
+ }
+ }
+ if( !m_pEndArr )
+ m_pEndArr.reset( new SwFootnoteFrames ); // deleted from the SwLayouter
+ m_pEndArr->push_back( pFootnote );
+}
+
+void SwEndnoter::InsertEndnotes()
+{
+ if( !m_pSect )
+ return;
+ if( !m_pEndArr || m_pEndArr->empty() )
+ {
+ m_pSect = nullptr;
+ return;
+ }
+ OSL_ENSURE( m_pSect->Lower() && m_pSect->Lower()->IsFootnoteBossFrame(),
+ "InsertEndnotes: Where's my column?" );
+ SwFrame* pRef = m_pSect->FindLastContent( SwFindMode::MyLast );
+ SwFootnoteBossFrame *pBoss = pRef ? pRef->FindFootnoteBossFrame()
+ : static_cast<SwFootnoteBossFrame*>(m_pSect->Lower());
+ pBoss->MoveFootnotes_( *m_pEndArr );
+ m_pEndArr.reset();
+ m_pSect = nullptr;
+}
+
+SwLooping::SwLooping( SwPageFrame const * pPage )
+{
+ OSL_ENSURE( pPage, "Where's my page?" );
+ mnMinPage = pPage->GetPhyPageNum();
+ mnMaxPage = mnMinPage;
+ mnCount = 0;
+ mnLoopControlStage = 0;
+}
+
+void SwLooping::Drastic( SwFrame* pFrame )
+{
+ while( pFrame )
+ {
+ pFrame->ValidateThisAndAllLowers( mnLoopControlStage );
+ pFrame = pFrame->GetNext();
+ }
+}
+
+void SwLooping::Control( SwPageFrame* pPage )
+{
+ if( !pPage )
+ return;
+ const sal_uInt16 nNew = pPage->GetPhyPageNum();
+ if (nNew > mnMaxPage)
+ mnMaxPage = nNew;
+ if (nNew < mnMinPage)
+ {
+ mnMinPage = nNew;
+ mnMaxPage = nNew;
+ mnCount = 0;
+ mnLoopControlStage = 0;
+ }
+ else if (nNew > mnMinPage + 2)
+ {
+ mnMinPage = nNew - 2;
+ mnMaxPage = nNew;
+ mnCount = 0;
+ mnLoopControlStage = 0;
+ }
+ else if (++mnCount > LOOP_DETECT)
+ {
+#if OSL_DEBUG_LEVEL > 1
+ static bool bNoLouie = false;
+ if( bNoLouie )
+ return;
+
+ // FME 2007-08-30 #i81146# new loop control
+ OSL_ENSURE( 0 != mnLoopControlStage, "Looping Louie: Stage 1!" );
+ OSL_ENSURE( 1 != mnLoopControlStage, "Looping Louie: Stage 2!!" );
+ OSL_ENSURE( 2 > mnLoopControlStage, "Looping Louie: Stage 3!!!" );
+#endif
+
+ Drastic( pPage->Lower() );
+ if (nNew > mnMinPage && pPage->GetPrev())
+ Drastic( static_cast<SwPageFrame*>(pPage->GetPrev())->Lower() );
+ if (nNew < mnMaxPage && pPage->GetNext())
+ Drastic( static_cast<SwPageFrame*>(pPage->GetNext())->Lower() );
+
+ ++mnLoopControlStage;
+ mnCount = 0;
+ }
+}
+
+SwLayouter::SwLayouter()
+{
+}
+
+SwLayouter::~SwLayouter()
+{
+}
+
+void SwLayouter::CollectEndnotes_( SwSectionFrame* pSect )
+{
+ if( !mpEndnoter )
+ mpEndnoter.reset(new SwEndnoter( this ));
+ mpEndnoter->CollectEndnotes( pSect );
+}
+
+bool SwLayouter::HasEndnotes() const
+{
+ return mpEndnoter->HasEndnotes();
+}
+
+void SwLayouter::CollectEndnote( SwFootnoteFrame* pFootnote )
+{
+ mpEndnoter->CollectEndnote( pFootnote );
+}
+
+void SwLayouter::InsertEndnotes( SwSectionFrame const * pSect )
+{
+ if( !mpEndnoter || mpEndnoter->GetSect() != pSect )
+ return;
+ mpEndnoter->InsertEndnotes();
+}
+
+void SwLayouter::LoopControl( SwPageFrame* pPage )
+{
+ OSL_ENSURE( mpLooping, "Looping: Lost control" );
+ mpLooping->Control( pPage );
+}
+
+void SwLayouter::LoopingLouieLight( const SwDoc& rDoc, const SwTextFrame& rFrame )
+{
+ if ( mpLooping && mpLooping->IsLoopingLouieLight() )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ OSL_FAIL( "Looping Louie (Light): Fixating fractious frame" );
+#endif
+ SwLayouter::InsertMovedFwdFrame( rDoc, rFrame, rFrame.FindPageFrame()->GetPhyPageNum() );
+ }
+}
+
+bool SwLayouter::StartLooping( SwPageFrame const * pPage )
+{
+ if( mpLooping )
+ return false;
+ mpLooping.reset(new SwLooping( pPage ));
+ return true;
+}
+
+void SwLayouter::EndLoopControl()
+{
+ mpLooping.reset();
+}
+
+void SwLayouter::CollectEndnotes( SwDoc* pDoc, SwSectionFrame* pSect )
+{
+ assert(pDoc && "No doc, no fun");
+ if( !pDoc->getIDocumentLayoutAccess().GetLayouter() )
+ pDoc->getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
+ pDoc->getIDocumentLayoutAccess().GetLayouter()->CollectEndnotes_( pSect );
+}
+
+bool SwLayouter::Collecting( SwDoc* pDoc, SwSectionFrame const * pSect, SwFootnoteFrame* pFootnote )
+{
+ if( !pDoc->getIDocumentLayoutAccess().GetLayouter() )
+ return false;
+ SwLayouter *pLayouter = pDoc->getIDocumentLayoutAccess().GetLayouter();
+ if( pLayouter->mpEndnoter && pLayouter->mpEndnoter->GetSect() && pSect &&
+ ( pLayouter->mpEndnoter->GetSect()->IsAnFollow( pSect ) ||
+ pSect->IsAnFollow( pLayouter->mpEndnoter->GetSect() ) ) )
+ {
+ if( pFootnote )
+ pLayouter->CollectEndnote( pFootnote );
+ return true;
+ }
+ return false;
+}
+
+bool SwLayouter::StartLoopControl( SwDoc* pDoc, SwPageFrame const *pPage )
+{
+ OSL_ENSURE( pDoc, "No doc, no fun" );
+ if( !pDoc->getIDocumentLayoutAccess().GetLayouter() )
+ pDoc->getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
+ return !pDoc->getIDocumentLayoutAccess().GetLayouter()->mpLooping &&
+ pDoc->getIDocumentLayoutAccess().GetLayouter()->StartLooping( pPage );
+}
+
+// #i28701#
+// methods to manage text frames, which are moved forward by the positioning
+// of its anchored objects
+void SwLayouter::ClearMovedFwdFrames( const SwDoc& _rDoc )
+{
+ if ( _rDoc.getIDocumentLayoutAccess().GetLayouter() &&
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
+ {
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->Clear();
+ }
+}
+
+void SwLayouter::InsertMovedFwdFrame( const SwDoc& _rDoc,
+ const SwTextFrame& _rMovedFwdFrameByObjPos,
+ const sal_uInt32 _nToPageNum )
+{
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
+ }
+
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
+ {
+ const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames.reset(
+ new SwMovedFwdFramesByObjPos());
+ }
+
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->Insert( _rMovedFwdFrameByObjPos,
+ _nToPageNum );
+}
+
+// #i40155#
+void SwLayouter::RemoveMovedFwdFrame( const SwDoc& _rDoc,
+ const SwTextFrame& _rTextFrame )
+{
+ sal_uInt32 nDummy;
+ if ( SwLayouter::FrameMovedFwdByObjPos( _rDoc, _rTextFrame, nDummy ) )
+ {
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->Remove( _rTextFrame );
+ }
+}
+
+bool SwLayouter::FrameMovedFwdByObjPos( const SwDoc& _rDoc,
+ const SwTextFrame& _rTextFrame,
+ sal_uInt32& _ornToPageNum )
+{
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ _ornToPageNum = 0;
+ return false;
+ }
+ else if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
+ {
+ _ornToPageNum = 0;
+ return false;
+ }
+ else
+ {
+ return _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->
+ FrameMovedFwdByObjPos( _rTextFrame, _ornToPageNum );
+ }
+}
+
+// #i26945#
+bool SwLayouter::DoesRowContainMovedFwdFrame( const SwDoc& _rDoc,
+ const SwRowFrame& _rRowFrame )
+{
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ return false;
+ }
+ else if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
+ {
+ return false;
+ }
+ else
+ {
+ return _rDoc.getIDocumentLayoutAccess().GetLayouter()->
+ mpMovedFwdFrames->DoesRowContainMovedFwdFrame( _rRowFrame );
+ }
+}
+
+// #i35911#
+void SwLayouter::ClearObjsTmpConsiderWrapInfluence( const SwDoc& _rDoc )
+{
+ if ( _rDoc.getIDocumentLayoutAccess().GetLayouter() &&
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl )
+ {
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl->Clear();
+ }
+}
+
+void SwLayouter::InsertObjForTmpConsiderWrapInfluence(
+ const SwDoc& _rDoc,
+ SwAnchoredObject& _rAnchoredObj )
+{
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
+ }
+
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl )
+ {
+ const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl.reset(
+ new SwObjsMarkedAsTmpConsiderWrapInfluence());
+ }
+
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl->Insert( _rAnchoredObj );
+}
+
+void SwLayouter::RemoveObjForTmpConsiderWrapInfluence(
+ const SwDoc& _rDoc,
+ SwAnchoredObject& _rAnchoredObj )
+{
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ return;
+
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl )
+ return;
+
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl->Remove( _rAnchoredObj );
+}
+
+
+void LOOPING_LOUIE_LIGHT( bool bCondition, const SwTextFrame& rTextFrame )
+{
+ if ( bCondition )
+ {
+ const SwDoc& rDoc = *rTextFrame.GetAttrSet()->GetDoc();
+ if ( rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ const_cast<SwDoc&>(rDoc).getIDocumentLayoutAccess().GetLayouter()->LoopingLouieLight( rDoc, rTextFrame );
+ }
+ }
+}
+
+// #i65250#
+bool SwLayouter::MoveBwdSuppressed( const SwDoc& p_rDoc,
+ const SwFlowFrame& p_rFlowFrame,
+ const SwLayoutFrame& p_rNewUpperFrame )
+{
+ bool bMoveBwdSuppressed( false );
+
+ if ( !p_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ const_cast<SwDoc&>(p_rDoc).getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
+ }
+
+ // create hash map key
+ tMoveBwdLayoutInfoKey aMoveBwdLayoutInfo;
+ aMoveBwdLayoutInfo.mnFrameId = p_rFlowFrame.GetFrame().GetFrameId();
+ aMoveBwdLayoutInfo.mnNewUpperPosX = p_rNewUpperFrame.getFrameArea().Pos().X();
+ aMoveBwdLayoutInfo.mnNewUpperPosY = p_rNewUpperFrame.getFrameArea().Pos().Y();
+ aMoveBwdLayoutInfo.mnNewUpperWidth = p_rNewUpperFrame.getFrameArea().Width();
+ aMoveBwdLayoutInfo.mnNewUpperHeight = p_rNewUpperFrame.getFrameArea().Height();
+ SwRectFnSet aRectFnSet(&p_rNewUpperFrame);
+ const SwFrame* pLastLower( p_rNewUpperFrame.Lower() );
+ while ( pLastLower && pLastLower->GetNext() )
+ {
+ pLastLower = pLastLower->GetNext();
+ }
+ aMoveBwdLayoutInfo.mnFreeSpaceInNewUpper =
+ pLastLower
+ ? aRectFnSet.BottomDist( pLastLower->getFrameArea(), aRectFnSet.GetPrtBottom(p_rNewUpperFrame) )
+ : aRectFnSet.GetHeight(p_rNewUpperFrame.getFrameArea());
+
+ // check for moving backward suppress threshold
+ const sal_uInt16 cMoveBwdCountSuppressThreshold = 20;
+ if ( ++const_cast<SwDoc&>(p_rDoc).getIDocumentLayoutAccess().GetLayouter()->maMoveBwdLayoutInfo[ aMoveBwdLayoutInfo ] >
+ cMoveBwdCountSuppressThreshold )
+ {
+ bMoveBwdSuppressed = true;
+ }
+
+ return bMoveBwdSuppressed;
+}
+
+void SwLayouter::ClearMoveBwdLayoutInfo( const SwDoc& _rDoc )
+{
+ if ( _rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().GetLayouter()->maMoveBwdLayoutInfo.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/legacyitem.cxx b/sw/source/core/layout/legacyitem.cxx
new file mode 100644
index 000000000..281b71d83
--- /dev/null
+++ b/sw/source/core/layout/legacyitem.cxx
@@ -0,0 +1,78 @@
+/* -*- 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 <legacyitem.hxx>
+#include <tools/stream.hxx>
+#include <sal/log.hxx>
+#include <fmtornt.hxx>
+
+namespace legacy::SwFormatVert
+{
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SwFormatVertOrient& rItem, SvStream& rStrm, sal_uInt16 nVersionAbusedAsSize)
+ {
+ SwTwips yPos(0);
+ sal_Int16 orient(0);
+ sal_Int16 relation(0);
+
+ switch (nVersionAbusedAsSize)
+ {
+ // compatibility hack for Table Auto Format: SwTwips is "long" :(
+ // (this means that the file format is platform dependent)
+ case 14:
+ {
+ sal_Int64 n(0);
+ rStrm.ReadInt64(n);
+ yPos = n;
+ break;
+ }
+ case 10:
+ {
+ sal_Int32 n(0);
+ rStrm.ReadInt32(n);
+ yPos = n;
+ break;
+ }
+ default:
+ SAL_WARN("sw.core", "SwFormatVertOrient::Create: unknown size");
+ }
+
+ rStrm.ReadInt16( orient ).ReadInt16( relation );
+
+ rItem.SetPos(yPos);
+ rItem.SetVertOrient(orient);
+ rItem.SetRelationOrient(relation);
+ }
+
+ SvStream& Store(const SwFormatVertOrient& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ if constexpr(sizeof(rItem.GetPos()) >= 8)
+ rStrm.WriteInt64(rItem.GetPos());
+ else
+ rStrm.WriteInt32(rItem.GetPos());
+ rStrm.WriteInt16(rItem.GetVertOrient()).WriteInt16(rItem.GetRelationOrient());
+ return rStrm;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/movedfwdfrmsbyobjpos.cxx b/sw/source/core/layout/movedfwdfrmsbyobjpos.cxx
new file mode 100644
index 000000000..7aba4b74a
--- /dev/null
+++ b/sw/source/core/layout/movedfwdfrmsbyobjpos.cxx
@@ -0,0 +1,90 @@
+/* -*- 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 <movedfwdfrmsbyobjpos.hxx>
+#include <txtfrm.hxx>
+#include <rowfrm.hxx>
+#include <pagefrm.hxx>
+#include <calbck.hxx>
+#include <ndtxt.hxx>
+
+SwMovedFwdFramesByObjPos::SwMovedFwdFramesByObjPos()
+{
+}
+
+SwMovedFwdFramesByObjPos::~SwMovedFwdFramesByObjPos()
+{
+ Clear();
+}
+
+void SwMovedFwdFramesByObjPos::Insert( const SwTextFrame& _rMovedFwdFrameByObjPos,
+ const sal_uInt32 _nToPageNum )
+{
+ maMovedFwdFrames.emplace(_rMovedFwdFrameByObjPos.GetTextNodeFirst(), _nToPageNum);
+}
+
+void SwMovedFwdFramesByObjPos::Remove( const SwTextFrame& _rTextFrame )
+{
+ maMovedFwdFrames.erase(_rTextFrame.GetTextNodeFirst());
+}
+
+bool SwMovedFwdFramesByObjPos::FrameMovedFwdByObjPos( const SwTextFrame& _rTextFrame,
+ sal_uInt32& _ornToPageNum ) const
+{
+ // sw_redlinehide: assumption: this wants to uniquely identify all
+ // SwTextFrame belonging to the same paragraph, so just use first one as key
+ auto aIter = maMovedFwdFrames.find( _rTextFrame.GetTextNodeFirst() );
+ if ( maMovedFwdFrames.end() != aIter )
+ {
+ _ornToPageNum = (*aIter).second;
+ return true;
+ }
+
+ return false;
+}
+
+// #i26945#
+bool SwMovedFwdFramesByObjPos::DoesRowContainMovedFwdFrame( const SwRowFrame& _rRowFrame ) const
+{
+ bool bDoesRowContainMovedFwdFrame( false );
+
+ const sal_uInt32 nPageNumOfRow = _rRowFrame.FindPageFrame()->GetPhyPageNum();
+
+ for ( const auto & rEntry : maMovedFwdFrames )
+ {
+ if ( rEntry.second >= nPageNumOfRow )
+ {
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aFrameIter(*rEntry.first);
+ for( SwTextFrame* pTextFrame = aFrameIter.First(); pTextFrame; pTextFrame = aFrameIter.Next() )
+ {
+ // #115759# - assure that found text frame
+ // is the first one.
+ if ( _rRowFrame.IsAnLower( pTextFrame ) && !pTextFrame->GetIndPrev() )
+ {
+ bDoesRowContainMovedFwdFrame = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return bDoesRowContainMovedFwdFrame;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/newfrm.cxx b/sw/source/core/layout/newfrm.cxx
new file mode 100644
index 000000000..c09c55779
--- /dev/null
+++ b/sw/source/core/layout/newfrm.cxx
@@ -0,0 +1,618 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <svx/svdpage.hxx>
+#include <osl/diagnose.h>
+#include <drawdoc.hxx>
+#include <fmtpdsc.hxx>
+#include <swtable.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <dflyobj.hxx>
+#include <frmtool.hxx>
+#include "virtoutp.hxx"
+#include <notxtfrm.hxx>
+#include <pagedesc.hxx>
+#include <viewimp.hxx>
+#include <hints.hxx>
+#include <viewopt.hxx>
+#include <set>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <DocumentLayoutManager.hxx>
+#include <DocumentRedlineManager.hxx>
+#include <ndindex.hxx>
+
+SwLayVout *SwRootFrame::s_pVout = nullptr;
+bool SwRootFrame::s_isInPaint = false;
+bool SwRootFrame::s_isNoVirDev = false;
+
+SwCache *SwFrame::spCache = nullptr;
+
+static tools::Long FirstMinusSecond( tools::Long nFirst, tools::Long nSecond )
+ { return nFirst - nSecond; }
+static tools::Long SecondMinusFirst( tools::Long nFirst, tools::Long nSecond )
+ { return nSecond - nFirst; }
+static tools::Long SwIncrement( tools::Long nA, tools::Long nAdd )
+ { return nA + nAdd; }
+static tools::Long SwDecrement( tools::Long nA, tools::Long nSub )
+ { return nA - nSub; }
+
+static SwRectFnCollection aHorizontal = {
+ /*.fnGetTop =*/&SwRect::Top_,
+ /*.fnGetBottom =*/&SwRect::Bottom_,
+ /*.fnGetLeft =*/&SwRect::Left_,
+ /*.fnGetRight =*/&SwRect::Right_,
+ /*.fnGetWidth =*/&SwRect::Width_,
+ /*.fnGetHeight =*/&SwRect::Height_,
+ /*.fnGetPos =*/&SwRect::TopLeft,
+ /*.fnGetSize =*/&SwRect::Size_,
+
+ /*.fnSetTop =*/&SwRect::Top_,
+ /*.fnSetBottom =*/&SwRect::Bottom_,
+ /*.fnSetLeft =*/&SwRect::Left_,
+ /*.fnSetRight =*/&SwRect::Right_,
+ /*.fnSetWidth =*/&SwRect::Width_,
+ /*.fnSetHeight =*/&SwRect::Height_,
+
+ /*.fnSubTop =*/&SwRect::SubTop,
+ /*.fnAddBottom =*/&SwRect::AddBottom,
+ /*.fnSubLeft =*/&SwRect::SubLeft,
+ /*.fnAddRight =*/&SwRect::AddRight,
+ /*.fnAddWidth =*/&SwRect::AddWidth,
+ /*.fnAddHeight =*/&SwRect::AddHeight,
+
+ /*.fnSetPosX =*/&SwRect::SetPosX,
+ /*.fnSetPosY =*/&SwRect::SetPosY,
+
+ /*.fnGetTopMargin =*/&SwFrame::GetTopMargin,
+ /*.fnGetBottomMargin =*/&SwFrame::GetBottomMargin,
+ /*.fnGetLeftMargin =*/&SwFrame::GetLeftMargin,
+ /*.fnGetRightMargin =*/&SwFrame::GetRightMargin,
+ /*.fnSetXMargins =*/&SwFrame::SetLeftRightMargins,
+ /*.fnSetYMargins =*/&SwFrame::SetTopBottomMargins,
+ /*.fnGetPrtTop =*/&SwFrame::GetPrtTop,
+ /*.fnGetPrtBottom =*/&SwFrame::GetPrtBottom,
+ /*.fnGetPrtLeft =*/&SwFrame::GetPrtLeft,
+ /*.fnGetPrtRight =*/&SwFrame::GetPrtRight,
+ /*.fnTopDist =*/&SwRect::GetTopDistance,
+ /*.fnBottomDist =*/&SwRect::GetBottomDistance,
+ /*.fnLeftDist =*/&SwRect::GetLeftDistance,
+ /*.fnRightDist =*/&SwRect::GetRightDistance,
+ /*.fnSetLimit =*/&SwFrame::SetMaxBottom,
+ /*.fnOverStep =*/&SwRect::OverStepBottom,
+
+ /*.fnSetPos =*/&SwRect::SetUpperLeftCorner,
+ /*.fnMakePos =*/&SwFrame::MakeBelowPos,
+ /*.fnXDiff =*/&FirstMinusSecond,
+ /*.fnYDiff =*/&FirstMinusSecond,
+ /*.fnXInc =*/&SwIncrement,
+ /*.fnYInc =*/&o3tl::saturating_add<tools::Long>,
+
+ /*.fnSetLeftAndWidth =*/&SwRect::SetLeftAndWidth,
+ /*.fnSetTopAndHeight =*/&SwRect::SetTopAndHeight
+};
+
+static SwRectFnCollection aVertical = {
+ /*.fnGetTop =*/&SwRect::Right_,
+ /*.fnGetBottom =*/&SwRect::Left_,
+ /*.fnGetLeft =*/&SwRect::Top_,
+ /*.fnGetRight =*/&SwRect::Bottom_,
+ /*.fnGetWidth =*/&SwRect::Height_,
+ /*.fnGetHeight =*/&SwRect::Width_,
+ /*.fnGetPos =*/&SwRect::TopRight,
+ /*.fnGetSize =*/&SwRect::SwappedSize,
+
+ /*.fnSetTop =*/&SwRect::Right_,
+ /*.fnSetBottom =*/&SwRect::Left_,
+ /*.fnSetLeft =*/&SwRect::Top_,
+ /*.fnSetRight =*/&SwRect::Bottom_,
+ /*.fnSetWidth =*/&SwRect::Height_,
+ /*.fnSetHeight =*/&SwRect::Width_,
+
+ /*.fnSubTop =*/&SwRect::AddRight,
+ /*.fnAddBottom =*/&SwRect::SubLeft,
+ /*.fnSubLeft =*/&SwRect::SubTop,
+ /*.fnAddRight =*/&SwRect::AddBottom,
+ /*.fnAddWidth =*/&SwRect::AddHeight,
+ /*.fnAddHeight =*/&SwRect::AddWidth,
+
+ /*.fnSetPosX =*/&SwRect::SetPosY,
+ /*.fnSetPosY =*/&SwRect::SetPosX,
+
+ /*.fnGetTopMargin =*/&SwFrame::GetRightMargin,
+ /*.fnGetBottomMargin =*/&SwFrame::GetLeftMargin,
+ /*.fnGetLeftMargin =*/&SwFrame::GetTopMargin,
+ /*.fnGetRightMargin =*/&SwFrame::GetBottomMargin,
+ /*.fnSetXMargins =*/&SwFrame::SetTopBottomMargins,
+ /*.fnSetYMargins =*/&SwFrame::SetRightLeftMargins,
+ /*.fnGetPrtTop =*/&SwFrame::GetPrtRight,
+ /*.fnGetPrtBottom =*/&SwFrame::GetPrtLeft,
+ /*.fnGetPrtLeft =*/&SwFrame::GetPrtTop,
+ /*.fnGetPrtRight =*/&SwFrame::GetPrtBottom,
+ /*.fnTopDist =*/&SwRect::GetRightDistance,
+ /*.fnBottomDist =*/&SwRect::GetLeftDistance,
+ /*.fnLeftDist =*/&SwRect::GetTopDistance,
+ /*.fnRightDist =*/&SwRect::GetBottomDistance,
+ /*.fnSetLimit =*/&SwFrame::SetMinLeft,
+ /*.fnOverStep =*/&SwRect::OverStepLeft,
+
+ /*.fnSetPos =*/&SwRect::SetUpperRightCorner,
+ /*.fnMakePos =*/&SwFrame::MakeLeftPos,
+ /*.fnXDiff =*/&FirstMinusSecond,
+ /*.fnYDiff =*/&SecondMinusFirst,
+ /*.fnXInc =*/&SwIncrement,
+ /*.fnYInc =*/&SwDecrement,
+
+ /*.fnSetLeftAndWidth =*/&SwRect::SetTopAndHeight,
+ /*.fnSetTopAndHeight =*/&SwRect::SetRightAndWidth
+};
+
+static SwRectFnCollection aVerticalLeftToRight = {
+ /*.fnGetTop =*/&SwRect::Left_,
+ /*.fnGetBottom =*/&SwRect::Right_,
+ /*.fnGetLeft =*/&SwRect::Top_,
+ /*.fnGetRight =*/&SwRect::Bottom_,
+ /*.fnGetWidth =*/&SwRect::Height_,
+ /*.fnGetHeight =*/&SwRect::Width_,
+ /*.fnGetPos =*/&SwRect::TopLeft,
+ /*.fnGetSize =*/&SwRect::SwappedSize,
+
+ /*.fnSetTop =*/&SwRect::Left_,
+ /*.fnSetBottom =*/&SwRect::Right_,
+ /*.fnSetLeft =*/&SwRect::Top_,
+ /*.fnSetRight =*/&SwRect::Bottom_,
+ /*.fnSetWidth =*/&SwRect::Height_,
+ /*.fnSetHeight =*/&SwRect::Width_,
+
+ /*.fnSubTop =*/&SwRect::SubLeft,
+ /*.fnAddBottom =*/&SwRect::AddRight,
+ /*.fnSubLeft =*/&SwRect::SubTop,
+ /*.fnAddRight =*/&SwRect::AddBottom,
+ /*.fnAddWidth =*/&SwRect::AddHeight,
+ /*.fnAddHeight =*/&SwRect::AddWidth,
+
+ /*.fnSetPosX =*/&SwRect::SetPosY,
+ /*.fnSetPosY =*/&SwRect::SetPosX,
+
+ /*.fnGetTopMargin =*/&SwFrame::GetLeftMargin,
+ /*.fnGetBottomMargin =*/&SwFrame::GetRightMargin,
+ /*.fnGetLeftMargin =*/&SwFrame::GetTopMargin,
+ /*.fnGetRightMargin =*/&SwFrame::GetBottomMargin,
+ /*.fnSetXMargins =*/&SwFrame::SetTopBottomMargins,
+ /*.fnSetYMargins =*/&SwFrame::SetLeftRightMargins,
+ /*.fnGetPrtTop =*/&SwFrame::GetPrtLeft,
+ /*.fnGetPrtBottom =*/&SwFrame::GetPrtRight,
+ /*.fnGetPrtLeft =*/&SwFrame::GetPrtTop,
+ /*.fnGetPrtRight =*/&SwFrame::GetPrtBottom,
+ /*.fnTopDist =*/&SwRect::GetLeftDistance,
+ /*.fnBottomDist =*/&SwRect::GetRightDistance,
+ /*.fnLeftDist =*/&SwRect::GetTopDistance,
+ /*.fnRightDist =*/&SwRect::GetBottomDistance,
+ /*.fnSetLimit =*/&SwFrame::SetMaxRight,
+ /*.fnOverStep =*/&SwRect::OverStepRight,
+
+ /*.fnSetPos =*/&SwRect::SetUpperLeftCorner,
+ /*.fnMakePos =*/&SwFrame::MakeRightPos,
+ /*.fnXDiff =*/&FirstMinusSecond,
+ /*.fnYDiff =*/&FirstMinusSecond,
+ /*.fnXInc =*/&SwIncrement,
+ /*.fnYInc =*/&SwIncrement,
+
+ /*.fnSetLeftAndWidth =*/&SwRect::SetTopAndHeight,
+ /*.fnSetTopAndHeight =*/&SwRect::SetLeftAndWidth
+};
+
+/**
+ * This is the same as horizontal, but rotated counter-clockwise by 90 degrees.
+ * This means logical top is physical left, bottom is right, left is bottom,
+ * finally right is top. Values map from logical to physical.
+ */
+static SwRectFnCollection aVerticalLeftToRightBottomToTop = {
+ /*.fnGetTop =*/&SwRect::Left_,
+ /*.fnGetBottom =*/&SwRect::Right_,
+ /*.fnGetLeft =*/&SwRect::Bottom_,
+ /*.fnGetRight =*/&SwRect::Top_,
+ /*.fnGetWidth =*/&SwRect::Height_,
+ /*.fnGetHeight =*/&SwRect::Width_,
+ /*.fnGetPos =*/&SwRect::BottomLeft,
+ /*.fnGetSize =*/&SwRect::SwappedSize,
+
+ /*.fnSetTop =*/&SwRect::Left_,
+ /*.fnSetBottom =*/&SwRect::Right_,
+ /*.fnSetLeft =*/&SwRect::Bottom_,
+ /*.fnSetRight =*/&SwRect::Top_,
+ /*.fnSetWidth =*/&SwRect::Height_,
+ /*.fnSetHeight =*/&SwRect::Width_,
+
+ /*.fnSubTop =*/&SwRect::SubLeft,
+ /*.fnAddBottom =*/&SwRect::AddRight,
+ /*.fnSubLeft =*/&SwRect::AddBottom,
+ /*.fnAddRight =*/&SwRect::SubTop,
+ /*.fnAddWidth =*/&SwRect::AddHeight,
+ /*.fnAddHeight =*/&SwRect::AddWidth,
+
+ /*.fnSetPosX =*/&SwRect::SetPosY,
+ /*.fnSetPosY =*/&SwRect::SetPosX,
+
+ /*.fnGetTopMargin =*/&SwFrame::GetLeftMargin,
+ /*.fnGetBottomMargin =*/&SwFrame::GetRightMargin,
+ /*.fnGetLeftMargin =*/&SwFrame::GetBottomMargin,
+ /*.fnGetRightMargin =*/&SwFrame::GetTopMargin,
+ /*.fnSetXMargins =*/&SwFrame::SetTopBottomMargins,
+ /*.fnSetYMargins =*/&SwFrame::SetLeftRightMargins,
+ /*.fnGetPrtTop =*/&SwFrame::GetPrtLeft,
+ /*.fnGetPrtBottom =*/&SwFrame::GetPrtRight,
+ /*.fnGetPrtLeft =*/&SwFrame::GetPrtBottom,
+ /*.fnGetPrtRight =*/&SwFrame::GetPrtTop,
+ /*.fnTopDist =*/&SwRect::GetLeftDistance,
+ /*.fnBottomDist =*/&SwRect::GetRightDistance,
+ /*.fnLeftDist =*/&SwRect::GetBottomDistance,
+ /*.fnRightDist =*/&SwRect::GetTopDistance,
+ /*.fnSetLimit =*/&SwFrame::SetMaxRight,
+ /*.fnOverStep =*/&SwRect::OverStepRight,
+
+ /*.fnSetPos =*/&SwRect::SetLowerLeftCorner,
+ /*.fnMakePos =*/&SwFrame::MakeRightPos,
+ /*.fnXDiff =*/&SecondMinusFirst,
+ /*.fnYDiff =*/&FirstMinusSecond,
+ /*.fnXInc =*/&SwDecrement,
+ /*.fnYInc =*/&SwIncrement,
+
+ /*.fnSetLeftAndWidth =*/&SwRect::SetBottomAndHeight,
+ /*.fnSetTopAndHeight =*/&SwRect::SetLeftAndWidth
+};
+
+SwRectFn fnRectHori = &aHorizontal;
+SwRectFn fnRectVert = &aVertical;
+SwRectFn fnRectVertL2R = &aVerticalLeftToRight;
+SwRectFn fnRectVertL2RB2T = &aVerticalLeftToRightBottomToTop;
+
+// #i65250#
+sal_uInt32 SwFrameAreaDefinition::snLastFrameId=0;
+
+
+void FrameInit()
+{
+ SwRootFrame::s_pVout = new SwLayVout();
+ SwCache *pNew = new SwCache( 100
+#ifdef DBG_UTIL
+ , "static SwBorderAttrs::pCache"
+#endif
+ );
+ SwFrame::SetCache( pNew );
+}
+
+void FrameFinit()
+{
+#if OSL_DEBUG_LEVEL > 0
+ // The cache may only contain null pointers at this time.
+ for( size_t n = SwFrame::GetCachePtr()->size(); n; )
+ if( (*SwFrame::GetCachePtr())[ --n ] )
+ {
+ SwCacheObj* pObj = (*SwFrame::GetCachePtr())[ n ];
+ OSL_ENSURE( !pObj, "Who didn't deregister?");
+ }
+#endif
+ delete SwRootFrame::s_pVout;
+ delete SwFrame::GetCachePtr();
+}
+
+// RootFrame::Everything that belongs to CurrShell
+
+CurrShell::CurrShell( SwViewShell *pNew )
+{
+ OSL_ENSURE( pNew, "insert 0-Shell?" );
+ pRoot = pNew->GetLayout();
+ if ( pRoot )
+ {
+ pPrev = pRoot->mpCurrShell;
+ pRoot->mpCurrShell = pNew;
+ pRoot->mpCurrShells->insert( this );
+ }
+ else
+ pPrev = nullptr;
+}
+
+CurrShell::~CurrShell()
+{
+ if ( pRoot )
+ {
+ pRoot->mpCurrShells->erase( this );
+ if ( pPrev )
+ pRoot->mpCurrShell = pPrev;
+ if ( pRoot->mpCurrShells->empty() && pRoot->mpWaitingCurrShell )
+ {
+ pRoot->mpCurrShell = pRoot->mpWaitingCurrShell;
+ pRoot->mpWaitingCurrShell = nullptr;
+ }
+ }
+}
+
+void SetShell( SwViewShell *pSh )
+{
+ SwRootFrame *pRoot = pSh->GetLayout();
+ if ( pRoot->mpCurrShells->empty() )
+ pRoot->mpCurrShell = pSh;
+ else
+ pRoot->mpWaitingCurrShell = pSh;
+}
+
+void SwRootFrame::DeRegisterShell( SwViewShell *pSh )
+{
+ // Activate some shell if possible
+ if ( mpCurrShell == pSh )
+ {
+ mpCurrShell = nullptr;
+ for(SwViewShell& rShell : pSh->GetRingContainer())
+ {
+ if(&rShell != pSh)
+ {
+ mpCurrShell = &rShell;
+ break;
+ }
+ }
+ }
+
+ // Doesn't matter anymore
+ if ( mpWaitingCurrShell == pSh )
+ mpWaitingCurrShell = nullptr;
+
+ // Remove references
+ for ( CurrShell *pC : *mpCurrShells )
+ {
+ if (pC->pPrev == pSh)
+ pC->pPrev = nullptr;
+ }
+}
+
+void InitCurrShells( SwRootFrame *pRoot )
+{
+ pRoot->mpCurrShells.reset( new SwCurrShells );
+}
+
+/*
+|* The RootFrame requests an own FrameFormat from the document, which it is
+|* going to delete again in the dtor. The own FrameFormat is derived from
+|* the passed FrameFormat.
+|*/
+SwRootFrame::SwRootFrame( SwFrameFormat *pFormat, SwViewShell * pSh ) :
+ SwLayoutFrame( pFormat->GetDoc()->MakeFrameFormat(
+ "Root", pFormat ), nullptr ),
+ mnViewWidth( -1 ),
+ mnColumns( 0 ),
+ mbBookMode( false ),
+ mbSidebarChanged( false ),
+ mbNeedGrammarCheck( false ),
+ mbCheckSuperfluous( false ),
+ mbIdleFormat( true ),
+ mbBrowseWidthValid( false ),
+ mbTurboAllowed( true ),
+ mbAssertFlyPages( true ),
+ mbTableUpdateInProgress( false ),
+ mbIsVirtPageNum( false ),
+ mbIsNewLayout( true ),
+ mbCallbackActionEnabled ( false ),
+ mbLayoutFreezed ( false ),
+ mbHideRedlines(pFormat->GetDoc()->GetDocumentRedlineManager().IsHideRedlines()),
+ m_FieldmarkMode(pSh->GetViewOptions()->IsFieldName()
+ ? sw::FieldmarkMode::ShowCommand
+ : sw::FieldmarkMode::ShowResult),
+ mnBrowseWidth(MIN_BROWSE_WIDTH),
+ mpTurbo( nullptr ),
+ mpLastPage( nullptr ),
+ mpCurrShell( pSh ),
+ mpWaitingCurrShell( nullptr ),
+ mpDrawPage( nullptr ),
+ mnPhyPageNums( 0 ),
+ mnAccessibleShells( 0 )
+{
+ mnFrameType = SwFrameType::Root;
+ setRootFrame( this );
+}
+
+void SwRootFrame::Init( SwFrameFormat* pFormat )
+{
+ InitCurrShells( this );
+
+ IDocumentTimerAccess& rTimerAccess = pFormat->getIDocumentTimerAccess();
+ IDocumentLayoutAccess& rLayoutAccess = pFormat->getIDocumentLayoutAccess();
+ IDocumentFieldsAccess& rFieldsAccess = pFormat->getIDocumentFieldsAccess();
+ const IDocumentSettingAccess& rSettingAccess = pFormat->getIDocumentSettingAccess();
+ rTimerAccess.StopIdling();
+ // For creating the Flys by MakeFrames()
+ rLayoutAccess.SetCurrentViewShell( GetCurrShell() );
+ mbCallbackActionEnabled = false; // needs to be set to true before leaving!
+
+ SwDrawModel* pMd = pFormat->getIDocumentDrawModelAccess().GetDrawModel();
+ if ( pMd )
+ {
+ // Disable "multiple layout"
+ mpDrawPage = pMd->GetPage(0);
+
+ mpDrawPage->SetSize( getFrameArea().SSize() );
+ }
+
+ // Initialize the layout: create pages, link content with Content etc.
+ // First, initialize some stuff, then get hold of the first
+ // node (which will be needed for the PageDesc).
+
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwNodeIndex aIndex( *pDoc->GetNodes().GetEndOfContent().StartOfSectionNode() );
+ SwContentNode *pNode = pDoc->GetNodes().GoNextSection( &aIndex, true, false );
+ // #123067# pNode = 0 can really happen
+ SwTableNode *pTableNd= pNode ? pNode->FindTableNode() : nullptr;
+
+ // Get hold of PageDesc (either via FrameFormat of the first node or the initial one).
+ SwPageDesc *pDesc = nullptr;
+ ::std::optional<sal_uInt16> oPgNum;
+
+ if ( pTableNd )
+ {
+ const SwFormatPageDesc &rDesc = pTableNd->GetTable().GetFrameFormat()->GetPageDesc();
+ pDesc = const_cast<SwPageDesc*>(rDesc.GetPageDesc());
+ //#19104# respect the page number offset!!
+ oPgNum = rDesc.GetNumOffset();
+ if (oPgNum)
+ mbIsVirtPageNum = true;
+ }
+ else if ( pNode )
+ {
+ const SwFormatPageDesc &rDesc = pNode->GetSwAttrSet().GetPageDesc();
+ pDesc = const_cast<SwPageDesc*>(rDesc.GetPageDesc());
+ //#19104# respect the page number offset!!
+ oPgNum = rDesc.GetNumOffset();
+ if (oPgNum)
+ mbIsVirtPageNum = true;
+ }
+ else
+ mbIsVirtPageNum = false;
+ if ( !pDesc )
+ pDesc = &pDoc->GetPageDesc( 0 );
+
+ // Create a page and put it in the layout
+ // The first page is always a right-page and always a first-page
+ SwPageFrame* pPage = ::InsertNewPage(
+ *pDesc, /*pUpper=*/this, /*isRightPage=*/true, /*bFirst=*/true, /*bInsertEmpty=*/false,
+ /*bFootnote=*/false, /*pSibling=*/nullptr, /*bVeryFirstPage=*/true);
+
+ // Find the first page in the Bodytext section.
+ SwLayoutFrame *pLay = pPage->FindBodyCont();
+ while( pLay->Lower() )
+ pLay = static_cast<SwLayoutFrame*>(pLay->Lower());
+
+ SwNodeIndex aTmp( *pDoc->GetNodes().GetEndOfContent().StartOfSectionNode(), 1 );
+ ::InsertCnt_( pLay, pDoc, aTmp.GetIndex(), true );
+
+ if( rSettingAccess.get(DocumentSettingId::GLOBAL_DOCUMENT) )
+ rFieldsAccess.UpdateRefFields();
+ //b6433357: Update page fields after loading
+ if ( !mpCurrShell || !mpCurrShell->Imp()->IsUpdateExpFields() )
+ {
+ SwDocPosUpdate aMsgHint( pPage->getFrameArea().Top() );
+ rFieldsAccess.UpdatePageFields( &aMsgHint );
+ }
+
+ rTimerAccess.StartIdling();
+ mbCallbackActionEnabled = true;
+
+ SwViewShell *pViewSh = GetCurrShell();
+ if (pViewSh)
+ mbNeedGrammarCheck = pViewSh->GetViewOptions()->IsOnlineSpell();
+}
+
+void SwRootFrame::DestroyImpl()
+{
+ mbTurboAllowed = false;
+ mpTurbo = nullptr;
+
+ SwFrameFormat *pRegisteredInNonConst = static_cast<SwFrameFormat*>(GetDep());
+ if ( pRegisteredInNonConst )
+ {
+ SwDoc *pDoc = pRegisteredInNonConst->GetDoc();
+ pDoc->DelFrameFormat( pRegisteredInNonConst );
+ // do this before calling RemoveFootnotes() because footnotes
+ // can contain anchored objects
+ pDoc->GetDocumentLayoutManager().ClearSwLayouterEntries();
+ }
+
+ mpDestroy.reset();
+
+ // Remove references
+ for ( auto& rpCurrShell : *mpCurrShells )
+ rpCurrShell->pRoot = nullptr;
+
+ mpCurrShells.reset();
+
+ // Some accessible shells are left => problems on second SwFrame::Destroy call
+ assert(0 == mnAccessibleShells);
+
+ // fdo#39510 crash on document close with footnotes
+ // Object ownership in writer and esp. in layout are a mess: Before the
+ // document/layout split SwDoc and SwRootFrame were essentially one object
+ // and magically/uncleanly worked around their common destruction by call
+ // to SwDoc::IsInDtor() -- even from the layout. As of now destruction of
+ // the layout proceeds forward through the frames. Since SwTextFootnote::DelFrames
+ // also searches backwards to find the master of footnotes, they must be
+ // considered to be owned by the SwRootFrame and also be destroyed here,
+ // before tearing down the (now footnote free) rest of the layout.
+ RemoveFootnotes(nullptr, false, true);
+
+ SwLayoutFrame::DestroyImpl();
+}
+
+SwRootFrame::~SwRootFrame()
+{
+}
+
+void SwRootFrame::RemoveMasterObjs( SdrPage *pPg )
+{
+ // Remove all master objects from the Page. But don't delete!
+ for( size_t i = pPg ? pPg->GetObjCount() : 0; i; )
+ {
+ SdrObject* pObj = pPg->GetObj( --i );
+ if( dynamic_cast< const SwFlyDrawObj *>( pObj ) != nullptr )
+ pPg->RemoveObject( i );
+ }
+}
+
+void SwRootFrame::AllCheckPageDescs() const
+{
+ if ( !IsLayoutFreezed() )
+ CheckPageDescs( const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(Lower())) );
+}
+
+void SwRootFrame::AllInvalidateAutoCompleteWords() const
+{
+ SwPageFrame *pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(Lower()));
+ while ( pPage )
+ {
+ pPage->InvalidateAutoCompleteWords();
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+}
+
+void SwRootFrame::AllAddPaintRect() const
+{
+ GetCurrShell()->AddPaintRect( getFrameArea() );
+}
+
+void SwRootFrame::AllRemoveFootnotes()
+{
+ RemoveFootnotes();
+}
+
+void SwRootFrame::AllInvalidateSmartTagsOrSpelling(bool bSmartTags) const
+{
+ SwPageFrame *pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(Lower()));
+ while ( pPage )
+ {
+ if ( bSmartTags )
+ pPage->InvalidateSmartTags();
+
+ pPage->InvalidateSpelling();
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objectformatter.cxx b/sw/source/core/layout/objectformatter.cxx
new file mode 100644
index 000000000..15ca544a2
--- /dev/null
+++ b/sw/source/core/layout/objectformatter.cxx
@@ -0,0 +1,478 @@
+/* -*- 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 "objectformattertxtfrm.hxx"
+#include "objectformatterlayfrm.hxx"
+#include <anchoreddrawobject.hxx>
+#include <sortedobjs.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <flyfrms.hxx>
+#include <txtfrm.hxx>
+#include <layact.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <osl/diagnose.h>
+
+#include <vector>
+
+// --> #i26945# - Additionally the type of the anchor text frame
+// is collected - by type is meant 'master' or 'follow'.
+class SwPageNumAndTypeOfAnchors
+{
+ private:
+ struct tEntry
+ {
+ SwAnchoredObject* mpAnchoredObj;
+ sal_uInt32 mnPageNumOfAnchor;
+ bool mbAnchoredAtMaster;
+ };
+
+ std::vector< tEntry > maObjList;
+
+ public:
+ SwPageNumAndTypeOfAnchors()
+ {
+ }
+
+ void Collect( SwAnchoredObject& _rAnchoredObj )
+ {
+ tEntry aNewEntry;
+ aNewEntry.mpAnchoredObj = &_rAnchoredObj;
+ // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
+ // is replaced by method <FindPageFrameOfAnchor()>. It's return value
+ // have to be checked.
+ SwPageFrame* pPageFrameOfAnchor = _rAnchoredObj.FindPageFrameOfAnchor();
+ if ( pPageFrameOfAnchor )
+ {
+ aNewEntry.mnPageNumOfAnchor = pPageFrameOfAnchor->GetPhyPageNum();
+ }
+ else
+ {
+ aNewEntry.mnPageNumOfAnchor = 0;
+ }
+ // --> #i26945# - collect type of anchor
+ SwTextFrame* pAnchorCharFrame = _rAnchoredObj.FindAnchorCharFrame();
+ if ( pAnchorCharFrame )
+ {
+ aNewEntry.mbAnchoredAtMaster = !pAnchorCharFrame->IsFollow();
+ }
+ else
+ {
+ aNewEntry.mbAnchoredAtMaster = true;
+ }
+ maObjList.push_back( aNewEntry );
+ }
+
+ SwAnchoredObject* operator[]( sal_uInt32 _nIndex )
+ {
+ return maObjList[_nIndex].mpAnchoredObj;
+ }
+
+ sal_uInt32 GetPageNum( sal_uInt32 _nIndex ) const
+ {
+ return maObjList[_nIndex].mnPageNumOfAnchor;
+ }
+
+ // --> #i26945#
+ bool AnchoredAtMaster( sal_uInt32 _nIndex )
+ {
+ return maObjList[_nIndex].mbAnchoredAtMaster;
+ }
+
+ sal_uInt32 Count() const
+ {
+ return maObjList.size();
+ }
+};
+
+SwObjectFormatter::SwObjectFormatter( const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction,
+ const bool _bCollectPgNumOfAnchors )
+ : mrPageFrame( _rPageFrame ),
+ mbConsiderWrapOnObjPos( _rPageFrame.GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) ),
+ mpLayAction( _pLayAction ),
+ // --> #i26945#
+ mpPgNumAndTypeOfAnchors( _bCollectPgNumOfAnchors ? new SwPageNumAndTypeOfAnchors() : nullptr )
+{
+}
+
+SwObjectFormatter::~SwObjectFormatter()
+{
+}
+
+std::unique_ptr<SwObjectFormatter> SwObjectFormatter::CreateObjFormatter(
+ SwFrame& _rAnchorFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction )
+{
+ std::unique_ptr<SwObjectFormatter> pObjFormatter;
+ if ( _rAnchorFrame.IsTextFrame() )
+ {
+ pObjFormatter = SwObjectFormatterTextFrame::CreateObjFormatter(
+ static_cast<SwTextFrame&>(_rAnchorFrame),
+ _rPageFrame, _pLayAction );
+ }
+ else if ( _rAnchorFrame.IsLayoutFrame() )
+ {
+ pObjFormatter = SwObjectFormatterLayFrame::CreateObjFormatter(
+ static_cast<SwLayoutFrame&>(_rAnchorFrame),
+ _rPageFrame, _pLayAction );
+ }
+ else
+ {
+ OSL_FAIL( "<SwObjectFormatter::CreateObjFormatter(..)> - unexpected type of anchor frame" );
+ }
+
+ return pObjFormatter;
+}
+
+/** method to format all floating screen objects at the given anchor frame
+*/
+bool SwObjectFormatter::FormatObjsAtFrame( SwFrame& _rAnchorFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction )
+{
+ bool bSuccess( true );
+
+ // create corresponding object formatter
+ std::unique_ptr<SwObjectFormatter> pObjFormatter =
+ SwObjectFormatter::CreateObjFormatter( _rAnchorFrame, _rPageFrame, _pLayAction );
+
+ if ( pObjFormatter )
+ {
+ // format anchored floating screen objects
+ bSuccess = pObjFormatter->DoFormatObjs();
+ }
+
+ return bSuccess;
+}
+
+/** method to format a given floating screen object
+*/
+bool SwObjectFormatter::FormatObj( SwAnchoredObject& _rAnchoredObj,
+ SwFrame* _pAnchorFrame,
+ const SwPageFrame* _pPageFrame )
+{
+ bool bSuccess( true );
+
+ OSL_ENSURE( _pAnchorFrame || _rAnchoredObj.GetAnchorFrame(),
+ "<SwObjectFormatter::FormatObj(..)> - missing anchor frame" );
+ SwFrame& rAnchorFrame = _pAnchorFrame ? *_pAnchorFrame : *(_rAnchoredObj.AnchorFrame());
+
+ OSL_ENSURE( _pPageFrame || rAnchorFrame.FindPageFrame(),
+ "<SwObjectFormatter::FormatObj(..)> - missing page frame" );
+ const SwPageFrame& rPageFrame = _pPageFrame ? *_pPageFrame : *(rAnchorFrame.FindPageFrame());
+
+ // create corresponding object formatter
+ std::unique_ptr<SwObjectFormatter> pObjFormatter =
+ SwObjectFormatter::CreateObjFormatter( rAnchorFrame, rPageFrame, nullptr/*_pLayAction*/ );
+
+ if ( pObjFormatter )
+ {
+ // format given floating screen object
+ // --> #i40147# - check for moved forward anchor frame
+ bSuccess = pObjFormatter->DoFormatObj( _rAnchoredObj, true );
+ }
+
+ return bSuccess;
+}
+
+/** helper method for method <FormatObj_(..)> - performs the intrinsic format
+ of the layout of the given layout frame and all its lower layout frames.
+
+ #i28701#
+ IMPORTANT NOTE:
+ Method corresponds to methods <SwLayAction::FormatLayoutFly(..)> and
+ <SwLayAction::FormatLayout(..)>. Thus, its code for the formatting have
+ to be synchronised.
+*/
+void SwObjectFormatter::FormatLayout_( SwLayoutFrame& _rLayoutFrame )
+{
+ _rLayoutFrame.Calc(_rLayoutFrame.getRootFrame()->GetCurrShell()->GetOut());
+
+ SwFrame* pLowerFrame = _rLayoutFrame.Lower();
+ while ( pLowerFrame )
+ {
+ if ( pLowerFrame->IsLayoutFrame() )
+ {
+ FormatLayout_( *static_cast<SwLayoutFrame*>(pLowerFrame) );
+ }
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+}
+
+/** helper method for method <FormatObj_(..)> - performs the intrinsic
+ format of the content of the given floating screen object.
+
+ #i28701#
+*/
+void SwObjectFormatter::FormatObjContent( SwAnchoredObject& _rAnchoredObj )
+{
+ if ( !_rAnchoredObj.DynCastFlyFrame() )
+ {
+ // only Writer fly frames have content
+ return;
+ }
+
+ SwFlyFrame& rFlyFrame = static_cast<SwFlyFrame&>(_rAnchoredObj);
+ SwContentFrame* pContent = rFlyFrame.ContainsContent();
+
+ while ( pContent )
+ {
+ // format content
+ pContent->OptCalc();
+
+ // format floating screen objects at content text frame
+ // #i23129#, #i36347# - pass correct page frame to
+ // the object formatter
+ if ( pContent->IsTextFrame() &&
+ !SwObjectFormatter::FormatObjsAtFrame( *pContent,
+ *(pContent->FindPageFrame()),
+ GetLayAction() ) )
+ {
+ // restart format with first content
+ pContent = rFlyFrame.ContainsContent();
+ continue;
+ }
+
+ // continue with next content
+ pContent = pContent->GetNextContentFrame();
+ }
+}
+
+/** performs the intrinsic format of a given floating screen object and its content.
+
+ #i28701#
+*/
+void SwObjectFormatter::FormatObj_( SwAnchoredObject& _rAnchoredObj )
+{
+ // collect anchor object and its 'anchor' page number, if requested
+ if ( mpPgNumAndTypeOfAnchors )
+ {
+ mpPgNumAndTypeOfAnchors->Collect( _rAnchoredObj );
+ }
+
+ if ( auto pFlyFrame = _rAnchoredObj.DynCastFlyFrame() )
+ {
+ // --> #i34753# - reset flag, which prevents a positioning
+ if ( pFlyFrame->IsFlyLayFrame() )
+ {
+ static_cast<SwFlyLayFrame*>(pFlyFrame)->SetNoMakePos( false );
+ }
+
+ // #i81146# new loop control
+ int nLoopControlRuns = 0;
+ const int nLoopControlMax = 15;
+
+ do {
+ if ( mpLayAction )
+ {
+ mpLayAction->FormatLayoutFly( pFlyFrame );
+ // --> consider, if the layout action
+ // has to be restarted due to a delete of a page frame.
+ if ( mpLayAction->IsAgain() )
+ {
+ break;
+ }
+ }
+ else
+ {
+ FormatLayout_( *pFlyFrame );
+ }
+ // --> #i34753# - prevent further positioning, if
+ // to-page|to-fly anchored Writer fly frame is already clipped.
+ if ( pFlyFrame->IsFlyLayFrame() && pFlyFrame->IsClipped() )
+ {
+ static_cast<SwFlyLayFrame*>(pFlyFrame)->SetNoMakePos( true );
+ }
+ // #i23129#, #i36347# - pass correct page frame
+ // to the object formatter
+ SwObjectFormatter::FormatObjsAtFrame( *pFlyFrame,
+ *(pFlyFrame->FindPageFrame()),
+ mpLayAction );
+ if ( mpLayAction )
+ {
+ mpLayAction->FormatFlyContent( pFlyFrame );
+ // --> consider, if the layout action
+ // has to be restarted due to a delete of a page frame.
+ if ( mpLayAction->IsAgain() )
+ {
+ break;
+ }
+ }
+ else
+ {
+ FormatObjContent( *pFlyFrame );
+ }
+
+ if ( ++nLoopControlRuns >= nLoopControlMax )
+ {
+ OSL_FAIL( "LoopControl in SwObjectFormatter::FormatObj_: Stage 3!!!" );
+ pFlyFrame->ValidateThisAndAllLowers( 2 );
+ nLoopControlRuns = 0;
+ }
+
+ // --> #i57917#
+ // stop formatting of anchored object, if restart of layout process is requested.
+ } while ( !pFlyFrame->isFrameAreaDefinitionValid() &&
+ !_rAnchoredObj.RestartLayoutProcess() &&
+ pFlyFrame->GetAnchorFrame() == &GetAnchorFrame() );
+ }
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rAnchoredObj) != nullptr )
+ {
+ _rAnchoredObj.MakeObjPos();
+ }
+}
+
+/** invokes the intrinsic format method for all floating screen objects,
+ anchored at anchor frame on the given page frame
+
+ #i28701#
+ #i26945# - for format of floating screen objects for
+ follow text frames, the 'master' text frame is passed to the method.
+ Thus, the objects, whose anchor character is inside the follow text
+ frame can be formatted.
+*/
+bool SwObjectFormatter::FormatObjsAtFrame_( SwTextFrame* _pMasterTextFrame )
+{
+ // --> #i26945#
+ SwFrame* pAnchorFrame( nullptr );
+ if ( GetAnchorFrame().IsTextFrame() &&
+ static_cast<SwTextFrame&>(GetAnchorFrame()).IsFollow() &&
+ _pMasterTextFrame )
+ {
+ pAnchorFrame = _pMasterTextFrame;
+ }
+ else
+ {
+ pAnchorFrame = &GetAnchorFrame();
+ }
+ if ( !pAnchorFrame->GetDrawObjs() )
+ {
+ // nothing to do, if no floating screen object is registered at the anchor frame.
+ return true;
+ }
+
+ bool bSuccess( true );
+
+ for ( size_t i = 0; i < pAnchorFrame->GetDrawObjs()->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pAnchorFrame->GetDrawObjs())[i];
+
+ // check, if object's anchor is on the given page frame or
+ // object is registered at the given page frame.
+ // --> #i26945# - check, if the anchor character of the
+ // anchored object is located in a follow text frame. If this anchor
+ // follow text frame differs from the given anchor frame, the given
+ // anchor frame is a 'master' text frame of the anchor follow text frame.
+ // If the anchor follow text frame is in the same body as its 'master'
+ // text frame, do not format the anchored object.
+ // E.g., this situation can occur during the table row splitting algorithm.
+ SwTextFrame* pAnchorCharFrame = pAnchoredObj->FindAnchorCharFrame();
+ const bool bAnchoredAtFollowInSameBodyAsMaster =
+ pAnchorCharFrame && pAnchorCharFrame->IsFollow() &&
+ pAnchorCharFrame != pAnchoredObj->GetAnchorFrame() &&
+ pAnchorCharFrame->FindBodyFrame() ==
+ static_cast<SwTextFrame*>(pAnchoredObj->AnchorFrame())->FindBodyFrame();
+ if ( bAnchoredAtFollowInSameBodyAsMaster )
+ {
+ continue;
+ }
+ // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
+ // is replaced by method <FindPageFrameOfAnchor()>. It's return value
+ // have to be checked.
+ SwPageFrame* pPageFrameOfAnchor = pAnchoredObj->FindPageFrameOfAnchor();
+ OSL_ENSURE( pPageFrameOfAnchor,
+ "<SwObjectFormatter::FormatObjsAtFrame_()> - missing page frame." );
+ // --> #i26945#
+ if ( pPageFrameOfAnchor && pPageFrameOfAnchor == &mrPageFrame )
+ {
+ // if format of object fails, stop formatting and pass fail to
+ // calling method via the return value.
+ if ( !DoFormatObj( *pAnchoredObj ) )
+ {
+ bSuccess = false;
+ break;
+ }
+
+ // considering changes at <pAnchorFrame->GetDrawObjs()> during
+ // format of the object.
+ if ( !pAnchorFrame->GetDrawObjs() ||
+ i > pAnchorFrame->GetDrawObjs()->size() )
+ {
+ break;
+ }
+ else
+ {
+ const size_t nActPosOfObj =
+ pAnchorFrame->GetDrawObjs()->ListPosOf( *pAnchoredObj );
+ if ( nActPosOfObj == pAnchorFrame->GetDrawObjs()->size() ||
+ nActPosOfObj > i )
+ {
+ --i;
+ }
+ else if ( nActPosOfObj < i )
+ {
+ i = nActPosOfObj;
+ }
+ }
+ }
+ } // end of loop on <pAnchorFrame->.GetDrawObjs()>
+
+ return bSuccess;
+}
+
+/** accessor to collected anchored object
+
+ #i28701#
+*/
+SwAnchoredObject* SwObjectFormatter::GetCollectedObj( const sal_uInt32 _nIndex )
+{
+ return mpPgNumAndTypeOfAnchors ? (*mpPgNumAndTypeOfAnchors)[_nIndex] : nullptr;
+}
+
+/** accessor to 'anchor' page number of collected anchored object
+
+ #i28701#
+*/
+sal_uInt32 SwObjectFormatter::GetPgNumOfCollected( const sal_uInt32 _nIndex )
+{
+ return mpPgNumAndTypeOfAnchors ? mpPgNumAndTypeOfAnchors->GetPageNum(_nIndex) : 0;
+}
+
+/** accessor to 'anchor' type of collected anchored object
+
+ #i26945#
+*/
+bool SwObjectFormatter::IsCollectedAnchoredAtMaster( const sal_uInt32 _nIndex )
+{
+ return mpPgNumAndTypeOfAnchors == nullptr
+ || mpPgNumAndTypeOfAnchors->AnchoredAtMaster(_nIndex);
+}
+
+/** accessor to total number of collected anchored objects
+
+ #i28701#
+*/
+sal_uInt32 SwObjectFormatter::CountOfCollected()
+{
+ return mpPgNumAndTypeOfAnchors ? mpPgNumAndTypeOfAnchors->Count() : 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objectformatterlayfrm.cxx b/sw/source/core/layout/objectformatterlayfrm.cxx
new file mode 100644
index 000000000..a9b5201b3
--- /dev/null
+++ b/sw/source/core/layout/objectformatterlayfrm.cxx
@@ -0,0 +1,188 @@
+/* -*- 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 "objectformatterlayfrm.hxx"
+#include <anchoredobject.hxx>
+#include <sortedobjs.hxx>
+#include <pagefrm.hxx>
+
+#include <layact.hxx>
+#include <osl/diagnose.h>
+
+SwObjectFormatterLayFrame::SwObjectFormatterLayFrame( SwLayoutFrame& _rAnchorLayFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction )
+ : SwObjectFormatter( _rPageFrame, _pLayAction ),
+ mrAnchorLayFrame( _rAnchorLayFrame )
+{
+}
+
+SwObjectFormatterLayFrame::~SwObjectFormatterLayFrame()
+{
+}
+
+std::unique_ptr<SwObjectFormatterLayFrame> SwObjectFormatterLayFrame::CreateObjFormatter(
+ SwLayoutFrame& _rAnchorLayFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction )
+{
+ if ( !_rAnchorLayFrame.IsPageFrame() &&
+ !_rAnchorLayFrame.IsFlyFrame() )
+ {
+ OSL_FAIL( "<SwObjectFormatterLayFrame::CreateObjFormatter(..)> - unexpected type of anchor frame " );
+ return nullptr;
+ }
+
+ std::unique_ptr<SwObjectFormatterLayFrame> pObjFormatter;
+
+ // create object formatter, if floating screen objects are registered at
+ // given anchor layout frame.
+ if ( _rAnchorLayFrame.GetDrawObjs() ||
+ ( _rAnchorLayFrame.IsPageFrame() &&
+ static_cast<SwPageFrame&>(_rAnchorLayFrame).GetSortedObjs() ) )
+ {
+ pObjFormatter.reset(
+ new SwObjectFormatterLayFrame( _rAnchorLayFrame, _rPageFrame, _pLayAction ));
+ }
+
+ return pObjFormatter;
+}
+
+SwFrame& SwObjectFormatterLayFrame::GetAnchorFrame()
+{
+ return mrAnchorLayFrame;
+}
+
+// #i40147# - add parameter <_bCheckForMovedFwd>.
+// Not relevant for objects anchored at layout frame.
+bool SwObjectFormatterLayFrame::DoFormatObj( SwAnchoredObject& _rAnchoredObj,
+ const bool )
+{
+ FormatObj_( _rAnchoredObj );
+
+ // #124218# - consider that the layout action has to be
+ // restarted due to a deleted page frame.
+ return GetLayAction() == nullptr || !GetLayAction()->IsAgain();
+}
+
+bool SwObjectFormatterLayFrame::DoFormatObjs()
+{
+ bool bSuccess = FormatObjsAtFrame_();
+
+ if ( bSuccess && GetAnchorFrame().IsPageFrame() )
+ {
+ // anchor layout frame is a page frame.
+ // Thus, format also all anchored objects, which are registered at
+ // this page frame, whose 'anchor' isn't on this page frame and whose
+ // anchor frame is valid.
+ bSuccess = AdditionalFormatObjsOnPage();
+ }
+
+ return bSuccess;
+}
+
+/** method to format all anchored objects, which are registered at
+ the page frame, whose 'anchor' isn't on this page frame and whose
+ anchor frame is valid.
+
+ OD 2004-07-02 #i28701#
+*/
+bool SwObjectFormatterLayFrame::AdditionalFormatObjsOnPage()
+{
+ if ( !GetAnchorFrame().IsPageFrame() )
+ {
+ OSL_FAIL( "<SwObjectFormatterLayFrame::AdditionalFormatObjsOnPage()> - mis-usage of method, call only for anchor frames of type page frame" );
+ return true;
+ }
+
+ // #124218# - consider, if the layout action
+ // has to be restarted due to a delete of a page frame.
+ if ( GetLayAction() && GetLayAction()->IsAgain() )
+ {
+ return false;
+ }
+
+ SwPageFrame& rPageFrame = static_cast<SwPageFrame&>(GetAnchorFrame());
+
+ if ( !rPageFrame.GetSortedObjs() )
+ {
+ // nothing to do, if no floating screen object is registered at the anchor frame.
+ return true;
+ }
+
+ bool bSuccess( true );
+
+ for ( size_t i = 0; i < rPageFrame.GetSortedObjs()->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*rPageFrame.GetSortedObjs())[i];
+
+ // #i51941# - do not format object, which are anchored
+ // inside or at fly frame.
+ if ( pAnchoredObj->GetAnchorFrame()->FindFlyFrame() )
+ {
+ continue;
+ }
+ // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
+ // is replaced by method <FindPageFrameOfAnchor()>. It's return value
+ // have to be checked.
+ SwPageFrame* pPageFrameOfAnchor = pAnchoredObj->FindPageFrameOfAnchor();
+ // #i26945# - check, if the page frame of the
+ // object's anchor frame isn't the given page frame
+ OSL_ENSURE( pPageFrameOfAnchor,
+ "<SwObjectFormatterLayFrame::AdditionalFormatObjsOnPage()> - missing page frame" );
+ if ( pPageFrameOfAnchor &&
+ // #i35911#
+ pPageFrameOfAnchor->GetPhyPageNum() < rPageFrame.GetPhyPageNum() )
+ {
+ // if format of object fails, stop formatting and pass fail to
+ // calling method via the return value.
+ if ( !DoFormatObj( *pAnchoredObj ) )
+ {
+ bSuccess = false;
+ break;
+ }
+
+ // considering changes at <GetAnchorFrame().GetDrawObjs()> during
+ // format of the object.
+ if ( !rPageFrame.GetSortedObjs() ||
+ i > rPageFrame.GetSortedObjs()->size() )
+ {
+ break;
+ }
+ else
+ {
+ const size_t nActPosOfObj =
+ rPageFrame.GetSortedObjs()->ListPosOf( *pAnchoredObj );
+ if ( nActPosOfObj == rPageFrame.GetSortedObjs()->size() ||
+ nActPosOfObj > i )
+ {
+ --i;
+ }
+ else if ( nActPosOfObj < i )
+ {
+ i = nActPosOfObj;
+ }
+ }
+ }
+ } // end of loop on <rPageFrame.GetSortedObjs()>
+
+ return bSuccess;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objectformatterlayfrm.hxx b/sw/source/core/layout/objectformatterlayfrm.hxx
new file mode 100644
index 000000000..9ece6f281
--- /dev/null
+++ b/sw/source/core/layout/objectformatterlayfrm.hxx
@@ -0,0 +1,70 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJECTFORMATTERLAYFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJECTFORMATTERLAYFRM_HXX
+
+#include <objectformatter.hxx>
+
+class SwLayoutFrame;
+
+// Format floating screen objects, which are anchored at a given anchor text frame
+// and registered at the given page frame.
+class SwObjectFormatterLayFrame : public SwObjectFormatter
+{
+ private:
+ // anchor layout frame
+ SwLayoutFrame& mrAnchorLayFrame;
+
+ SwObjectFormatterLayFrame( SwLayoutFrame& _rAnchorLayFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction );
+
+ /** method to format all anchored objects, which are registered at
+ the page frame, whose 'anchor' isn't on this page frame and whose
+ anchor frame is valid.
+
+ OD 2004-07-02 #i28701#
+
+ @return boolean
+ indicates, if format was successful
+ */
+ bool AdditionalFormatObjsOnPage();
+
+ protected:
+
+ virtual SwFrame& GetAnchorFrame() override;
+
+ public:
+ virtual ~SwObjectFormatterLayFrame() override;
+
+ // #i40147# - add parameter <_bCheckForMovedFwd>.
+ // Not relevant for objects anchored at layout frame.
+ virtual bool DoFormatObj( SwAnchoredObject& _rAnchoredObj,
+ const bool _bCheckForMovedFwd = false ) override;
+ virtual bool DoFormatObjs() override;
+
+ static std::unique_ptr<SwObjectFormatterLayFrame> CreateObjFormatter(
+ SwLayoutFrame& _rAnchorLayFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objectformattertxtfrm.cxx b/sw/source/core/layout/objectformattertxtfrm.cxx
new file mode 100644
index 000000000..6b2503d40
--- /dev/null
+++ b/sw/source/core/layout/objectformattertxtfrm.cxx
@@ -0,0 +1,971 @@
+/* -*- 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 "objectformattertxtfrm.hxx"
+#include <sortedobjs.hxx>
+#include <rootfrm.hxx>
+#include <anchoredobject.hxx>
+#include <txtfrm.hxx>
+#include <pagefrm.hxx>
+#include <rowfrm.hxx>
+#include <layouter.hxx>
+#include <fmtanchr.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <layact.hxx>
+#include <flyfrm.hxx>
+#include <ftnfrm.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+// little helper class to forbid follow formatting for the given text frame
+class SwForbidFollowFormat
+{
+private:
+ SwTextFrame& mrTextFrame;
+ const bool bOldFollowFormatAllowed;
+
+public:
+ explicit SwForbidFollowFormat( SwTextFrame& _rTextFrame )
+ : mrTextFrame( _rTextFrame ),
+ bOldFollowFormatAllowed( _rTextFrame.FollowFormatAllowed() )
+ {
+ mrTextFrame.ForbidFollowFormat();
+ }
+
+ ~SwForbidFollowFormat()
+ {
+ if ( bOldFollowFormatAllowed )
+ {
+ mrTextFrame.AllowFollowFormat();
+ }
+ }
+};
+
+}
+
+SwObjectFormatterTextFrame::SwObjectFormatterTextFrame( SwTextFrame& _rAnchorTextFrame,
+ const SwPageFrame& _rPageFrame,
+ SwTextFrame* _pMasterAnchorTextFrame,
+ SwLayAction* _pLayAction )
+ : SwObjectFormatter( _rPageFrame, _pLayAction, true ),
+ mrAnchorTextFrame( _rAnchorTextFrame ),
+ mpMasterAnchorTextFrame( _pMasterAnchorTextFrame )
+{
+}
+
+SwObjectFormatterTextFrame::~SwObjectFormatterTextFrame()
+{
+}
+
+std::unique_ptr<SwObjectFormatterTextFrame> SwObjectFormatterTextFrame::CreateObjFormatter(
+ SwTextFrame& _rAnchorTextFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction )
+{
+ std::unique_ptr<SwObjectFormatterTextFrame> pObjFormatter;
+
+ // determine 'master' of <_rAnchorTextFrame>, if anchor frame is a follow text frame.
+ SwTextFrame* pMasterOfAnchorFrame = nullptr;
+ if ( _rAnchorTextFrame.IsFollow() )
+ {
+ pMasterOfAnchorFrame = _rAnchorTextFrame.FindMaster();
+ while ( pMasterOfAnchorFrame && pMasterOfAnchorFrame->IsFollow() )
+ {
+ pMasterOfAnchorFrame = pMasterOfAnchorFrame->FindMaster();
+ }
+ }
+
+ // create object formatter, if floating screen objects are registered
+ // at anchor frame (or at 'master' anchor frame)
+ if ( _rAnchorTextFrame.GetDrawObjs() ||
+ ( pMasterOfAnchorFrame && pMasterOfAnchorFrame->GetDrawObjs() ) )
+ {
+ pObjFormatter.reset(
+ new SwObjectFormatterTextFrame( _rAnchorTextFrame, _rPageFrame,
+ pMasterOfAnchorFrame, _pLayAction ));
+ }
+
+ return pObjFormatter;
+}
+
+SwFrame& SwObjectFormatterTextFrame::GetAnchorFrame()
+{
+ return mrAnchorTextFrame;
+}
+
+// #i40147# - add parameter <_bCheckForMovedFwd>.
+bool SwObjectFormatterTextFrame::DoFormatObj( SwAnchoredObject& _rAnchoredObj,
+ const bool _bCheckForMovedFwd )
+{
+ // consider, if the layout action has to be
+ // restarted due to a delete of a page frame.
+ if ( GetLayAction() && GetLayAction()->IsAgain() )
+ {
+ return false;
+ }
+
+ bool bSuccess( true );
+
+ if ( _rAnchoredObj.IsFormatPossible() )
+ {
+ _rAnchoredObj.SetRestartLayoutProcess( false );
+
+ FormatObj_( _rAnchoredObj );
+ // consider, if the layout action has to be
+ // restarted due to a delete of a page frame.
+ if ( GetLayAction() && GetLayAction()->IsAgain() )
+ {
+ return false;
+ }
+
+ // check, if layout process has to be restarted.
+ // if yes, perform needed invalidations.
+
+ // no restart of layout process,
+ // if anchored object is anchored inside a Writer fly frame,
+ // its position is already locked, and it follows the text flow.
+ const bool bRestart =
+ _rAnchoredObj.RestartLayoutProcess() &&
+ !( _rAnchoredObj.PositionLocked() &&
+ _rAnchoredObj.GetAnchorFrame()->IsInFly() &&
+ _rAnchoredObj.GetFrameFormat().GetFollowTextFlow().GetValue() );
+ if ( bRestart )
+ {
+ bSuccess = false;
+ InvalidatePrevObjs( _rAnchoredObj );
+ InvalidateFollowObjs( _rAnchoredObj );
+ }
+
+ // format anchor text frame, if wrapping style influence of the object
+ // has to be considered and it's <NONE_SUCCESSIVE_POSITIONED>
+ // #i3317# - consider also anchored objects, whose
+ // wrapping style influence is temporarily considered.
+ // #i40147# - consider also anchored objects, for
+ // whose the check of a moved forward anchor frame is requested.
+ // revise decision made for i3317:
+ // anchored objects, whose wrapping style influence is temporarily considered,
+ // have to be considered in method <SwObjectFormatterTextFrame::DoFormatObjs()>
+ if ( bSuccess &&
+ _rAnchoredObj.ConsiderObjWrapInfluenceOnObjPos() &&
+ ( _bCheckForMovedFwd ||
+ _rAnchoredObj.GetFrameFormat().GetWrapInfluenceOnObjPos().
+ // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE
+ GetWrapInfluenceOnObjPos( true ) ==
+ // #i35017# - constant name has changed
+ text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE ) )
+ {
+ // #i26945# - check conditions for move forward of
+ // anchor text frame
+ // determine, if anchor text frame has previous frame
+ const bool bDoesAnchorHadPrev = ( mrAnchorTextFrame.GetIndPrev() != nullptr );
+
+ // #i40141# - use new method - it also formats the
+ // section the anchor frame is in.
+ FormatAnchorFrameForCheckMoveFwd();
+
+ // #i35911#
+ if ( _rAnchoredObj.HasClearedEnvironment() )
+ {
+ _rAnchoredObj.SetClearedEnvironment( true );
+ // #i44049# - consider, that anchor frame
+ // could already been marked to move forward.
+ SwPageFrame* pAnchorPageFrame( mrAnchorTextFrame.FindPageFrame() );
+ if ( pAnchorPageFrame != _rAnchoredObj.GetPageFrame() )
+ {
+ bool bInsert( true );
+ sal_uInt32 nToPageNum( 0 );
+ const SwDoc& rDoc = *(GetPageFrame().GetFormat()->GetDoc());
+ if ( SwLayouter::FrameMovedFwdByObjPos(
+ rDoc, mrAnchorTextFrame, nToPageNum ) )
+ {
+ if ( nToPageNum < pAnchorPageFrame->GetPhyPageNum() )
+ SwLayouter::RemoveMovedFwdFrame( rDoc, mrAnchorTextFrame );
+ else
+ bInsert = false;
+ }
+ if ( bInsert )
+ {
+ SwLayouter::InsertMovedFwdFrame( rDoc, mrAnchorTextFrame,
+ pAnchorPageFrame->GetPhyPageNum() );
+ mrAnchorTextFrame.InvalidatePos();
+ bSuccess = false;
+ InvalidatePrevObjs( _rAnchoredObj );
+ InvalidateFollowObjs( _rAnchoredObj );
+ }
+ else
+ {
+ OSL_FAIL( "<SwObjectFormatterTextFrame::DoFormatObj(..)> - anchor frame not marked to move forward" );
+ }
+ }
+ }
+ else if ( !mrAnchorTextFrame.IsFollow() && bDoesAnchorHadPrev )
+ {
+ // index of anchored object in collection of page numbers and
+ // anchor types
+ sal_uInt32 nIdx( CountOfCollected() );
+ OSL_ENSURE( nIdx > 0,
+ "<SwObjectFormatterTextFrame::DoFormatObj(..)> - anchored object not collected!?" );
+ --nIdx;
+
+ sal_uInt32 nToPageNum( 0 );
+ // #i43913#
+ bool bDummy( false );
+ bool bPageHasFlysAnchoredBelowThis(false);
+ // see how SwObjectFormatter::FormatObjsAtFrame_() checks
+ // "pPageFrameOfAnchor == &mrPageFrame" - only caller relevant for
+ // this subclass
+ assert(GetPageFrame().GetPhyPageNum() == GetPgNumOfCollected(nIdx));
+ if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition( *GetCollectedObj( nIdx ),
+ GetPageFrame(),
+ IsCollectedAnchoredAtMaster( nIdx ),
+ nToPageNum, bDummy,
+ bPageHasFlysAnchoredBelowThis))
+ {
+ // #i49987# - consider, that anchor frame
+ // could already been marked to move forward.
+ bool bInsert( true );
+ sal_uInt32 nMovedFwdToPageNum( 0 );
+ const SwDoc& rDoc = *(GetPageFrame().GetFormat()->GetDoc());
+ if ( SwLayouter::FrameMovedFwdByObjPos(
+ rDoc, mrAnchorTextFrame, nMovedFwdToPageNum ) )
+ {
+ if ( nMovedFwdToPageNum < nToPageNum )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::RemoveMovedFwdFrame(rDoc, mrAnchorTextFrame);
+ }
+ }
+ else
+ bInsert = false;
+ }
+ if ( bInsert )
+ {
+ // Indicate that anchor text frame has to move forward and
+ // invalidate its position to force a re-format.
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::InsertMovedFwdFrame(rDoc,
+ mrAnchorTextFrame, nToPageNum);
+ }
+ mrAnchorTextFrame.InvalidatePos();
+
+ // Indicate restart of the layout process
+ bSuccess = false;
+
+ // If needed, invalidate previous objects anchored at same anchor
+ // text frame.
+ InvalidatePrevObjs( _rAnchoredObj );
+
+ // Invalidate object and following objects for the restart of the
+ // layout process
+ InvalidateFollowObjs( _rAnchoredObj );
+ }
+ else
+ {
+ OSL_FAIL( "<SwObjectFormatterTextFrame::DoFormatObj(..)> - anchor frame not marked to move forward" );
+ }
+ }
+ }
+ // i40155# - mark anchor frame not to wrap around
+ // objects under the condition, that its follow contains all its text.
+ else if ( !mrAnchorTextFrame.IsFollow() &&
+ mrAnchorTextFrame.GetFollow() &&
+ mrAnchorTextFrame.GetFollow()->GetOffset() == TextFrameIndex(0))
+ {
+ SwLayouter::RemoveMovedFwdFrame(
+ *(mrAnchorTextFrame.FindPageFrame()->GetFormat()->GetDoc()),
+ mrAnchorTextFrame );
+ }
+ }
+ }
+
+ return bSuccess;
+}
+
+bool SwObjectFormatterTextFrame::DoFormatObjs()
+{
+ if ( !mrAnchorTextFrame.isFrameAreaDefinitionValid() )
+ {
+ if ( GetLayAction() &&
+ mrAnchorTextFrame.FindPageFrame() != &GetPageFrame() )
+ {
+ // notify layout action, thus is can restart the layout process on
+ // a previous page.
+ GetLayAction()->SetAgain(true);
+ }
+ else
+ {
+ // the anchor text frame has to be valid, thus assert.
+ OSL_FAIL( "<SwObjectFormatterTextFrame::DoFormatObjs()> called for invalidate anchor text frame." );
+ }
+
+ return false;
+ }
+
+ bool bSuccess( true );
+
+ if ( mrAnchorTextFrame.IsFollow() )
+ {
+ // Only floating screen objects anchored as-character are directly
+ // registered at a follow text frame. The other floating screen objects
+ // are registered at the 'master' anchor text frame.
+ // Thus, format the other floating screen objects through the 'master'
+ // anchor text frame
+ OSL_ENSURE( mpMasterAnchorTextFrame,
+ "SwObjectFormatterTextFrame::DoFormatObjs() - missing 'master' anchor text frame" );
+ bSuccess = FormatObjsAtFrame_( mpMasterAnchorTextFrame );
+
+ if ( bSuccess )
+ {
+ // format of as-character anchored floating screen objects - no failure
+ // expected on the format of these objects.
+ bSuccess = FormatObjsAtFrame_();
+ }
+ }
+ else
+ {
+ bSuccess = FormatObjsAtFrame_();
+ }
+
+ // consider anchored objects, whose wrapping style influence are temporarily
+ // considered.
+ if ( bSuccess &&
+ ( ConsiderWrapOnObjPos() ||
+ ( !mrAnchorTextFrame.IsFollow() &&
+ AtLeastOneObjIsTmpConsiderWrapInfluence() ) ) )
+ {
+ const bool bDoesAnchorHadPrev = ( mrAnchorTextFrame.GetIndPrev() != nullptr );
+
+ // Format anchor text frame after its objects are formatted.
+ // Note: The format of the anchor frame also formats the invalid
+ // previous frames of the anchor frame. The format of the previous
+ // frames is needed to get a correct result of format of the
+ // anchor frame for the following check for moved forward anchors
+ // #i40141# - use new method - it also formats the
+ // section the anchor frame is in.
+ FormatAnchorFrameForCheckMoveFwd();
+
+ sal_uInt32 nToPageNum( 0 );
+ // #i43913#
+ bool bInFollow( false );
+ bool bPageHasFlysAnchoredBelowThis(false);
+ SwAnchoredObject* pObj = nullptr;
+ if ( !mrAnchorTextFrame.IsFollow() )
+ {
+ pObj = GetFirstObjWithMovedFwdAnchor(
+ // #i35017# - constant name has changed
+ text::WrapInfluenceOnPosition::ONCE_CONCURRENT,
+ nToPageNum, bInFollow, bPageHasFlysAnchoredBelowThis );
+ }
+ // #i35911#
+ if ( pObj && pObj->HasClearedEnvironment() )
+ {
+ pObj->SetClearedEnvironment( true );
+ // #i44049# - consider, that anchor frame
+ // could already been marked to move forward.
+ SwPageFrame* pAnchorPageFrame( mrAnchorTextFrame.FindPageFrame() );
+ // #i43913# - consider, that anchor frame
+ // is a follow or is in a follow row, which will move forward.
+ if ( pAnchorPageFrame != pObj->GetPageFrame() ||
+ bInFollow )
+ {
+ bool bInsert( true );
+ sal_uInt32 nTmpToPageNum( 0 );
+ const SwDoc& rDoc = *(GetPageFrame().GetFormat()->GetDoc());
+ if ( SwLayouter::FrameMovedFwdByObjPos(
+ rDoc, mrAnchorTextFrame, nTmpToPageNum ) )
+ {
+ if ( nTmpToPageNum < pAnchorPageFrame->GetPhyPageNum() )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::RemoveMovedFwdFrame(rDoc, mrAnchorTextFrame);
+ }
+ }
+ else
+ bInsert = false;
+ }
+ if ( bInsert )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::InsertMovedFwdFrame(rDoc, mrAnchorTextFrame,
+ pAnchorPageFrame->GetPhyPageNum());
+ }
+ mrAnchorTextFrame.InvalidatePos();
+ bSuccess = false;
+ InvalidatePrevObjs( *pObj );
+ InvalidateFollowObjs( *pObj );
+ }
+ else
+ {
+ OSL_FAIL( "<SwObjectFormatterTextFrame::DoFormatObjs(..)> - anchor frame not marked to move forward" );
+ }
+ }
+ }
+ else if ( pObj && bDoesAnchorHadPrev )
+ {
+ // Object found, whose anchor is moved forward
+
+ // #i49987# - consider, that anchor frame
+ // could already been marked to move forward.
+ bool bInsert( true );
+ sal_uInt32 nMovedFwdToPageNum( 0 );
+ const SwDoc& rDoc = *(GetPageFrame().GetFormat()->GetDoc());
+ if ( SwLayouter::FrameMovedFwdByObjPos(
+ rDoc, mrAnchorTextFrame, nMovedFwdToPageNum ) )
+ {
+ if ( nMovedFwdToPageNum < nToPageNum )
+ SwLayouter::RemoveMovedFwdFrame( rDoc, mrAnchorTextFrame );
+ else
+ bInsert = false;
+ }
+ if ( bInsert )
+ {
+ // Indicate that anchor text frame has to move forward and
+ // invalidate its position to force a re-format.
+ SwLayouter::InsertMovedFwdFrame( rDoc, mrAnchorTextFrame, nToPageNum );
+ mrAnchorTextFrame.InvalidatePos();
+
+ // Indicate restart of the layout process
+ bSuccess = false;
+
+ // If needed, invalidate previous objects anchored at same anchor
+ // text frame.
+ InvalidatePrevObjs( *pObj );
+
+ // Invalidate object and following objects for the restart of the
+ // layout process
+ InvalidateFollowObjs( *pObj );
+ }
+ else
+ {
+ OSL_FAIL( "<SwObjectFormatterTextFrame::DoFormatObjs(..)> - anchor frame not marked to move forward" );
+ }
+ }
+ // #i40155# - mark anchor frame not to wrap around
+ // objects under the condition, that its follow contains all its text.
+ else if ( !mrAnchorTextFrame.IsFollow() &&
+ mrAnchorTextFrame.GetFollow() &&
+ mrAnchorTextFrame.GetFollow()->GetOffset() == TextFrameIndex(0))
+ {
+ SwLayouter::RemoveMovedFwdFrame(
+ *(mrAnchorTextFrame.FindPageFrame()->GetFormat()->GetDoc()),
+ mrAnchorTextFrame );
+ }
+ }
+
+ return bSuccess;
+}
+
+void SwObjectFormatterTextFrame::InvalidatePrevObjs( SwAnchoredObject& _rAnchoredObj )
+{
+ // invalidate all previous objects, whose wrapping influence on the object
+ // positioning is <NONE_CONCURRENT_POSITIONED>.
+ // Note: list of objects at anchor frame is sorted by this property.
+ if ( _rAnchoredObj.GetFrameFormat().GetWrapInfluenceOnObjPos().
+ // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE
+ GetWrapInfluenceOnObjPos( true ) !=
+ // #i35017# - constant name has changed
+ text::WrapInfluenceOnPosition::ONCE_CONCURRENT )
+ return;
+
+ const SwSortedObjs* pObjs = GetAnchorFrame().GetDrawObjs();
+ if ( !pObjs )
+ return;
+
+ // determine start index
+ size_t i = pObjs->ListPosOf( _rAnchoredObj );
+ while (i > 0)
+ {
+ --i;
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ if ( pAnchoredObj->GetFrameFormat().GetWrapInfluenceOnObjPos().
+ // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE
+ GetWrapInfluenceOnObjPos( true ) ==
+ // #i35017# - constant name has changed
+ text::WrapInfluenceOnPosition::ONCE_CONCURRENT )
+ {
+ pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
+ }
+ }
+}
+
+void SwObjectFormatterTextFrame::InvalidateFollowObjs( SwAnchoredObject& _rAnchoredObj )
+{
+ _rAnchoredObj.InvalidateObjPosForConsiderWrapInfluence();
+
+ const SwSortedObjs* pObjs = GetPageFrame().GetSortedObjs();
+ if ( pObjs )
+ {
+ // determine start index
+ for ( size_t i = pObjs->ListPosOf( _rAnchoredObj ) + 1; i < pObjs->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
+ }
+ }
+}
+
+SwAnchoredObject* SwObjectFormatterTextFrame::GetFirstObjWithMovedFwdAnchor(
+ const sal_Int16 _nWrapInfluenceOnPosition,
+ sal_uInt32& _noToPageNum,
+ bool& _boInFollow,
+ bool& o_rbPageHasFlysAnchoredBelowThis)
+{
+ // #i35017# - constant names have changed
+ OSL_ENSURE( _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE ||
+ _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ONCE_CONCURRENT,
+ "<SwObjectFormatterTextFrame::GetFirstObjWithMovedFwdAnchor(..)> - invalid value for parameter <_nWrapInfluenceOnPosition>" );
+
+ SwAnchoredObject* pRetAnchoredObj = nullptr;
+
+ sal_uInt32 i = 0;
+ for ( ; i < CountOfCollected(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = GetCollectedObj(i);
+ if ( pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() &&
+ pAnchoredObj->GetFrameFormat().GetWrapInfluenceOnObjPos().
+ // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE
+ GetWrapInfluenceOnObjPos( true ) == _nWrapInfluenceOnPosition )
+ {
+ // see how SwObjectFormatter::FormatObjsAtFrame_() checks
+ // "pPageFrameOfAnchor == &mrPageFrame" - only caller relevant for
+ // this subclass
+ assert(GetPageFrame().GetPhyPageNum() == GetPgNumOfCollected(i));
+ // #i26945# - use new method <_CheckMovedFwdCondition(..)>
+ // #i43913#
+ if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition( *GetCollectedObj( i ),
+ GetPageFrame(),
+ IsCollectedAnchoredAtMaster( i ),
+ _noToPageNum, _boInFollow,
+ o_rbPageHasFlysAnchoredBelowThis) )
+ {
+ pRetAnchoredObj = pAnchoredObj;
+ break;
+ }
+ }
+ }
+
+ return pRetAnchoredObj;
+}
+
+static SwRowFrame const* FindTopLevelRowFrame(SwFrame const*const pFrame)
+{
+ SwRowFrame * pRow = const_cast<SwFrame*>(pFrame)->FindRowFrame();
+ // looks like SwTabFrame has mbInfTab = true so go up 2 levels
+ while (pRow->GetUpper()->GetUpper()->IsInTab())
+ {
+ pRow = pRow->GetUpper()->GetUpper()->FindRowFrame();
+ }
+ return pRow;
+}
+
+static SwContentFrame const* FindFrameInBody(SwAnchoredObject const& rAnchored)
+{
+ SwFrame const*const pAnchor(rAnchored.GetAnchorFrame());
+ assert(pAnchor);
+ if (pAnchor->IsPageFrame() || pAnchor->FindFooterOrHeader())
+ {
+ return nullptr;
+ }
+ if (pAnchor->IsInFly())
+ {
+ return FindFrameInBody(*pAnchor->FindFlyFrame());
+ }
+ if (pAnchor->IsInFootnote())
+ {
+ return pAnchor->FindFootnoteFrame()->GetRef();
+ }
+ assert(pAnchor->IsInDocBody());
+ assert(pAnchor->IsContentFrame());
+ return static_cast<SwContentFrame const*>(pAnchor);
+}
+
+// #i58182#
+// - replace private method by corresponding static public method
+bool SwObjectFormatterTextFrame::CheckMovedFwdCondition(
+ SwAnchoredObject& _rAnchoredObj,
+ SwPageFrame const& rFromPageFrame,
+ const bool _bAnchoredAtMasterBeforeFormatAnchor,
+ sal_uInt32& _noToPageNum,
+ bool& _boInFollow,
+ bool& o_rbPageHasFlysAnchoredBelowThis)
+{
+ const sal_uInt32 _nFromPageNum(rFromPageFrame.GetPhyPageNum());
+ bool bAnchorIsMovedForward( false );
+
+ SwPageFrame* pPageFrameOfAnchor = _rAnchoredObj.FindPageFrameOfAnchor();
+ if ( pPageFrameOfAnchor )
+ {
+ const sal_uInt32 nPageNum = pPageFrameOfAnchor->GetPhyPageNum();
+ if ( nPageNum > _nFromPageNum )
+ {
+ _noToPageNum = nPageNum;
+ // Handling of special case:
+ // If anchor frame is move forward into a follow flow row,
+ // <_noToPageNum> is set to <_nFromPageNum + 1>, because it is
+ // possible that the anchor page frame isn't valid, because the
+ // page distance between master row and follow flow row is greater
+ // than 1.
+ if ( _noToPageNum > (_nFromPageNum + 1) )
+ {
+ SwFrame* pAnchorFrame = _rAnchoredObj.GetAnchorFrameContainingAnchPos();
+ if ( pAnchorFrame->IsInTab() &&
+ pAnchorFrame->IsInFollowFlowRow() )
+ {
+ _noToPageNum = _nFromPageNum + 1;
+ }
+ }
+ bAnchorIsMovedForward = true;
+ }
+ }
+ // #i26945# - check, if an at-paragraph|at-character
+ // anchored object is now anchored at a follow text frame, which will be
+ // on the next page. Also check, if an at-character anchored object
+ // is now anchored at a text frame, which is in a follow flow row,
+ // which will be on the next page.
+ if ( !bAnchorIsMovedForward &&
+ _bAnchoredAtMasterBeforeFormatAnchor &&
+ ((_rAnchoredObj.GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR) ||
+ (_rAnchoredObj.GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PARA)))
+ {
+ SwFrame* pAnchorFrame = _rAnchoredObj.GetAnchorFrameContainingAnchPos();
+ OSL_ENSURE( pAnchorFrame->IsTextFrame(),
+ "<SwObjectFormatterTextFrame::CheckMovedFwdCondition(..) - wrong type of anchor frame>" );
+ SwTextFrame* pAnchorTextFrame = static_cast<SwTextFrame*>(pAnchorFrame);
+ bool bCheck( false );
+ if ( pAnchorTextFrame->IsFollow() )
+ {
+ bCheck = true;
+ }
+ else if( pAnchorTextFrame->IsInTab() )
+ {
+ const SwRowFrame* pMasterRow = pAnchorTextFrame->IsInFollowFlowRow();
+ if ( pMasterRow &&
+ pMasterRow->FindPageFrame() == pPageFrameOfAnchor )
+ {
+ bCheck = true;
+ }
+ }
+ if ( bCheck )
+ {
+ // check, if found text frame will be on the next page
+ // by checking, if it's in a column, which has no next.
+ SwFrame* pColFrame = pAnchorTextFrame->FindColFrame();
+ while ( pColFrame && !pColFrame->GetNext() )
+ {
+ pColFrame = pColFrame->FindColFrame();
+ }
+ if ( !pColFrame || !pColFrame->GetNext() )
+ {
+ _noToPageNum = _nFromPageNum + 1;
+ bAnchorIsMovedForward = true;
+ // #i43913#
+ _boInFollow = true;
+ }
+ }
+ }
+
+ if (bAnchorIsMovedForward)
+ {
+ // tdf#138518 try to determine if there is a fly on page rFromPageFrame
+ // which is anchored in a frame that is "below" the anchor frame
+ // of _rAnchoredObj, such that it should move to the next page before
+ // _rAnchoredObj does
+ if (auto * pObjs = rFromPageFrame.GetSortedObjs())
+ {
+ for (SwAnchoredObject *const pObj : *pObjs)
+ {
+ SwPageFrame const*const pObjAnchorPage(pObj->FindPageFrameOfAnchor());
+ assert(pObjAnchorPage);
+ if ((pObjAnchorPage == &rFromPageFrame
+ ? _boInFollow // same-page but will move forward
+ : rFromPageFrame.GetPhyPageNum() < pObjAnchorPage->GetPhyPageNum())
+ && pObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ != RndStdIds::FLY_AS_CHAR)
+ {
+ if (pPageFrameOfAnchor->GetPhyPageNum() < pObjAnchorPage->GetPhyPageNum())
+ {
+ SAL_INFO("sw.layout", "SwObjectFormatterTextFrame::CheckMovedFwdCondition(): o_rbPageHasFlysAnchoredBelowThis because next page");
+ o_rbPageHasFlysAnchoredBelowThis = true;
+ break;
+ }
+ // on same page: check if it's in next-chain in the document body
+ // (in case both are in the same fly the flag must not be
+ // set because the whole fly moves at once)
+ SwContentFrame const*const pInBodyFrameObj(FindFrameInBody(*pObj));
+ SwContentFrame const*const pInBodyFrameAnchoredObj(FindFrameInBody(_rAnchoredObj));
+ if (pInBodyFrameObj && pInBodyFrameAnchoredObj)
+ {
+ bool isBreakMore(false);
+ // currently this ignores index of at-char flys
+ for (SwContentFrame const* pContentFrame = pInBodyFrameAnchoredObj->FindNextCnt();
+ pContentFrame;
+ pContentFrame = pContentFrame->FindNextCnt())
+ {
+ if (pInBodyFrameObj == pContentFrame)
+ {
+ // subsequent cells in a row are not automatically
+ // "below" and the row could potentially be split
+ // TODO refine check if needed
+ if (!pInBodyFrameAnchoredObj->IsInTab()
+ || FindTopLevelRowFrame(pInBodyFrameAnchoredObj)
+ != FindTopLevelRowFrame(pInBodyFrameAnchoredObj))
+ { // anchored in next chain on same page
+ SAL_INFO("sw.layout", "SwObjectFormatterTextFrame::CheckMovedFwdCondition(): o_rbPageHasFlysAnchoredBelowThis because next chain on same page");
+ o_rbPageHasFlysAnchoredBelowThis = true;
+ isBreakMore = true;
+ }
+ break;
+ }
+ }
+ if (isBreakMore)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return bAnchorIsMovedForward;
+}
+
+static void CleanupEmptyFootnoteFrame(SwFrame* pLowerFrame)
+{
+ // Calc on a SwTextFrame in a footnote can move it to the next page -
+ // deletion of the SwFootnoteFrame was disabled with SwFrameDeleteGuard
+ // but now we have to clean up empty footnote frames to prevent crashes.
+ // Note: check it at this level, not lower: both container and footnote
+ // can be deleted at the same time!
+ if (!pLowerFrame->IsFootnoteContFrame())
+ return;
+
+ for (SwFrame * pFootnote = pLowerFrame->GetLower(); pFootnote; )
+ {
+ assert(pFootnote->IsFootnoteFrame());
+ SwFrame *const pNextNote = pFootnote->GetNext();
+ if (!pFootnote->IsDeleteForbidden() && !pFootnote->GetLower() && !pFootnote->IsColLocked() &&
+ !static_cast<SwFootnoteFrame*>(pFootnote)->IsBackMoveLocked())
+ {
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ }
+ pFootnote = pNextNote;
+ }
+}
+
+// #i40140# - helper method to format layout frames used by
+// method <SwObjectFormatterTextFrame::FormatAnchorFrameForCheckMoveFwd()>
+// #i44049# - format till a certain lower frame, if provided.
+static void lcl_FormatContentOfLayoutFrame( SwLayoutFrame* pLayFrame,
+ SwFrame* pLastLowerFrame = nullptr )
+{
+ SwFrame* pLowerFrame = pLayFrame->GetLower();
+ while ( pLowerFrame )
+ {
+ // #i44049#
+ if ( pLastLowerFrame && pLowerFrame == pLastLowerFrame )
+ {
+ break;
+ }
+ if ( pLowerFrame->IsLayoutFrame() )
+ {
+ SwFrameDeleteGuard aCrudeHack(pLowerFrame); // ??? any issue setting this for non-footnote frames?
+ lcl_FormatContentOfLayoutFrame( static_cast<SwLayoutFrame*>(pLowerFrame),
+ pLastLowerFrame );
+ }
+ else
+ pLowerFrame->Calc(pLowerFrame->getRootFrame()->GetCurrShell()->GetOut());
+
+ // Calc on a SwTextFrame in a footnote can move it to the next page -
+ // deletion of the SwFootnoteFrame was disabled with SwFrameDeleteGuard
+ // but now we have to clean up empty footnote frames to prevent crashes.
+ // Note: check it at this level, not lower: both container and footnote
+ // can be deleted at the same time!
+ SwFrame *const pNext = pLowerFrame->GetNext();
+ CleanupEmptyFootnoteFrame(pLowerFrame);
+ pLowerFrame = pNext;
+ }
+}
+
+/** method to format given anchor text frame and its previous frames
+
+ #i56300#
+ Usage: Needed to check, if the anchor text frame is moved forward
+ due to the positioning and wrapping of its anchored objects, and
+ to format the frames, which have become invalid due to the anchored
+ object formatting in the iterative object positioning algorithm
+*/
+void SwObjectFormatterTextFrame::FormatAnchorFrameAndItsPrevs( SwTextFrame& _rAnchorTextFrame )
+{
+ // #i47014# - no format of section and previous columns
+ // for follow text frames.
+ if ( !_rAnchorTextFrame.IsFollow() )
+ {
+ // In case the anchor frame is in a column or section, format its
+ // previous frames first - but don't jump out of the current layout
+ // environment, e.g. from footnotes into the footnote boss.
+ SwFrame * pSectFrame(nullptr);
+ SwFrame * pColFrameOfAnchor(nullptr);
+ for (SwFrame* pUpper = _rAnchorTextFrame.GetUpper();
+ pUpper != nullptr; pUpper = pUpper->GetUpper())
+ {
+ if (pUpper->IsCellFrame())
+ {
+ break; // apparently nothing to be done?
+ }
+ if (pUpper->IsFootnoteFrame())
+ {
+ SAL_INFO_IF(pColFrameOfAnchor == nullptr && pUpper->FindColFrame(),
+ "sw.layout", "tdf#122894 skipping column for footnote in column");
+ break; // stop: prevent crash in case footnotes are being moved
+ }
+ if (pUpper->IsSctFrame())
+ {
+ pColFrameOfAnchor = nullptr;
+ pSectFrame = pUpper;
+ break;
+ }
+ if (pColFrameOfAnchor != nullptr)
+ { // parent of column not a section frame => column not in section
+ break;
+ }
+ if (pUpper->IsColumnFrame())
+ {
+ pColFrameOfAnchor = pUpper;
+ }
+ }
+
+ // if anchor frame is directly inside a section, format this section and
+ // its previous frames.
+ // Note: It's a very simple format without formatting objects.
+ if (pSectFrame)
+ {
+ assert(pSectFrame->IsSctFrame());
+ {
+ SwFrameDeleteGuard aDeleteGuard(&_rAnchorTextFrame);
+ // #i44049#
+ _rAnchorTextFrame.LockJoin();
+ SwFrame* pFrame = pSectFrame->GetUpper()->GetLower();
+ // #i49605# - section frame could move forward
+ // by the format of its previous frame.
+ // Thus, check for valid <pFrame>.
+ while ( pFrame && pFrame != pSectFrame )
+ {
+ SwFrameDeleteGuard aDeleteFrameGuard(pFrame);
+
+ if ( pFrame->IsLayoutFrame() )
+ lcl_FormatContentOfLayoutFrame( static_cast<SwLayoutFrame*>(pFrame) );
+ else
+ pFrame->Calc(pFrame->getRootFrame()->GetCurrShell()->GetOut());
+
+ pFrame = pFrame->GetNext();
+ }
+ lcl_FormatContentOfLayoutFrame( static_cast<SwLayoutFrame*>(pSectFrame),
+ &_rAnchorTextFrame );
+ // #i44049#
+ _rAnchorTextFrame.UnlockJoin();
+ }
+ }
+
+ // #i40140# - if anchor frame is inside a column,
+ // format the content of the previous columns.
+ // Note: It's a very simple format without formatting objects.
+ if (pColFrameOfAnchor)
+ {
+ assert(pColFrameOfAnchor->IsColumnFrame());
+ // #i44049#
+ _rAnchorTextFrame.LockJoin();
+ SwFrameDeleteGuard aDeleteGuard(&_rAnchorTextFrame);
+ SwFrame* pColFrame = pColFrameOfAnchor->GetUpper()->GetLower();
+ while ( pColFrame != pColFrameOfAnchor )
+ {
+ SwFrame* pFrame = pColFrame->GetLower();
+ while ( pFrame )
+ {
+ if ( pFrame->IsLayoutFrame() )
+ lcl_FormatContentOfLayoutFrame( static_cast<SwLayoutFrame*>(pFrame) );
+ else
+ pFrame->Calc(pFrame->getRootFrame()->GetCurrShell()->GetOut());
+
+ pFrame = pFrame->GetNext();
+ }
+
+ pColFrame = pColFrame->GetNext();
+ }
+ // #i44049#
+ _rAnchorTextFrame.UnlockJoin();
+ }
+ }
+
+ // format anchor frame - format of its follow not needed
+ // #i43255# - forbid follow format, only if anchor text
+ // frame is in table
+ if ( _rAnchorTextFrame.IsInTab() )
+ {
+ SwForbidFollowFormat aForbidFollowFormat( _rAnchorTextFrame );
+ _rAnchorTextFrame.Calc(_rAnchorTextFrame.getRootFrame()->GetCurrShell()->GetOut());
+ }
+ else
+ {
+ _rAnchorTextFrame.Calc(_rAnchorTextFrame.getRootFrame()->GetCurrShell()->GetOut());
+ }
+}
+
+/** method to format the anchor frame for checking of the move forward condition
+
+ #i40141#
+*/
+void SwObjectFormatterTextFrame::FormatAnchorFrameForCheckMoveFwd()
+{
+ SwObjectFormatterTextFrame::FormatAnchorFrameAndItsPrevs( mrAnchorTextFrame );
+}
+
+/** method to determine if at least one anchored object has state
+ <temporarily consider wrapping style influence> set.
+*/
+bool SwObjectFormatterTextFrame::AtLeastOneObjIsTmpConsiderWrapInfluence()
+{
+ bool bRet( false );
+
+ const SwSortedObjs* pObjs = GetAnchorFrame().GetDrawObjs();
+ if ( pObjs && pObjs->size() > 1 )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objectformattertxtfrm.hxx b/sw/source/core/layout/objectformattertxtfrm.hxx
new file mode 100644
index 000000000..25a7a7e92
--- /dev/null
+++ b/sw/source/core/layout/objectformattertxtfrm.hxx
@@ -0,0 +1,190 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJECTFORMATTERTXTFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJECTFORMATTERTXTFRM_HXX
+
+#include <objectformatter.hxx>
+#include <sal/types.h>
+
+class SwTextFrame;
+
+// #i28701#
+// Format floating screen objects, which are anchored at a given anchor text frame
+// and registered at the given page frame.
+class SwObjectFormatterTextFrame : public SwObjectFormatter
+{
+ private:
+ // anchor text frame
+ SwTextFrame& mrAnchorTextFrame;
+
+ // 'master' anchor text frame
+ SwTextFrame* mpMasterAnchorTextFrame;
+
+ SwObjectFormatterTextFrame( SwTextFrame& _rAnchorTextFrame,
+ const SwPageFrame& _rPageFrame,
+ SwTextFrame* _pMasterAnchorTextFrame,
+ SwLayAction* _pLayAction );
+
+ /** method to invalidate objects, anchored previous to given object at
+ the anchor text frame
+
+ @param _rAnchoredObj
+ reference to anchored object - objects, anchored previous to
+ this one will be invalidated.
+ */
+ void InvalidatePrevObjs( SwAnchoredObject& _rAnchoredObj );
+
+ /** method to invalidate objects, anchored after the given object at
+ the page frame
+
+ @param _rAnchoredObj
+ reference to anchored object - objects, anchored after this one will
+ be invalidated.
+ */
+ void InvalidateFollowObjs( SwAnchoredObject& _rAnchoredObj );
+
+ /** method to determine first anchored object, whose 'anchor is moved
+ forward'.
+
+ 'anchor (of an object) is moved forward', if the anchor frame
+ respectively the anchor character of the object isn't on the
+ proposed page frame. Instead its on a following page
+
+ #i26945# - For at-character anchored objects,
+ it has also to be checked, if the anchor character is in a follow
+ text frame, which would move to the next page.
+
+ #i43913# - add output parameter <_boInFollow>
+
+ @param _nWrapInfluenceOnPosition
+ input parameter - only object with this given wrapping style
+ influence are investigated.
+
+ @param _nFromPageNum
+ input parameter - number of page frame, the 'anchor' should be
+
+ @param _noToPageNum
+ output parameter - number of page frame, the 'anchor' of the returned
+ anchored object is.
+
+ @param _boInFollow
+ output parameter - boolean, indicating that anchor text frame is
+ currently on the same page, but it's a follow of in a follow row,
+ which will move forward. value only relevant, if method returns
+ an anchored object
+
+ @return SwAnchoredObject*
+ anchored object with a 'moved forward anchor'. If NULL, no such
+ anchored object is found.
+ */
+ SwAnchoredObject* GetFirstObjWithMovedFwdAnchor(
+ const sal_Int16 _nWrapInfluenceOnPosition,
+ sal_uInt32& _noToPageNum,
+ bool& _boInFollow,
+ bool& o_rbPageHasFlysAnchoredBelowThis);
+
+ /** method to format the anchor frame for checking of the move forward condition
+
+ #i40141#
+ */
+ void FormatAnchorFrameForCheckMoveFwd();
+
+ /** method to determine if at least one anchored object has state
+ <temporarily consider wrapping style influence> set.
+ */
+ bool AtLeastOneObjIsTmpConsiderWrapInfluence();
+
+ protected:
+
+ virtual SwFrame& GetAnchorFrame() override;
+
+ public:
+ virtual ~SwObjectFormatterTextFrame() override;
+
+ // #i40147# - add parameter <_bCheckForMovedFwd>.
+ virtual bool DoFormatObj( SwAnchoredObject& _rAnchoredObj,
+ const bool _bCheckForMovedFwd = false ) override;
+ virtual bool DoFormatObjs() override;
+
+ /** method to create an instance of <SwObjectFormatterTextFrame> is
+ necessary.
+ */
+ static std::unique_ptr<SwObjectFormatterTextFrame> CreateObjFormatter(
+ SwTextFrame& _rAnchorTextFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction );
+
+ /** method to format given anchor text frame and its previous frames
+
+ #i56300#
+ Usage: Needed to check, if the anchor text frame is moved forward
+ due to the positioning and wrapping of its anchored objects, and
+ to format the frames, which have become invalid due to the anchored
+ object formatting in the iterative object positioning algorithm
+
+ @param _rAnchorTextFrame
+ input parameter - reference to anchor text frame, which has to be
+ formatted including its previous frames of the page.
+ */
+ static void FormatAnchorFrameAndItsPrevs( SwTextFrame& _rAnchorTextFrame );
+
+ /** method to check the conditions, if 'anchor is moved forward'
+
+ #i26945#
+ #i43913# - add output parameter <_boInFollow>
+ #i58182# - replace method by a corresponding static
+ method, because it's needed for the iterative positioning algorithm.
+
+ @param _rAnchoredObj
+ input parameter - anchored object, for which the condition has to checked.
+
+ @param _nFromPageNum
+ input parameter - number of the page, on which the check is performed
+
+ @param _bAnchoredAtMasterBeforeFormatAnchor
+ input parameter - boolean indicating, that the given anchored object
+ was anchored at the master frame before the anchor frame has been
+ formatted.
+
+ @param _noToPageNum
+ output parameter - number of page frame, the 'anchor' of the returned
+ anchored object is.
+
+ @param _boInFollow
+ output parameter - boolean, indicating that anchor text frame is
+ currently on the same page, but it's a follow of in a follow row,
+ which will move forward. value only relevant, if method return <true>.
+ @param o_rbPageHasFlysAnchoredBelowThis
+ output parameter - indicates that the page has flys anchored
+ somewhere below the anchor of the passed _rAnchoredObj
+
+ @return boolean
+ indicating, if 'anchor is moved forward'
+ */
+ static bool CheckMovedFwdCondition( SwAnchoredObject& _rAnchoredObj,
+ SwPageFrame const& rFromPageFrame,
+ const bool _bAnchoredAtMasterBeforeFormatAnchor,
+ sal_uInt32& _noToPageNum,
+ bool& _boInFollow,
+ bool& o_rbPageHasFlysAnchoredBelowThis);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objstmpconsiderwrapinfl.cxx b/sw/source/core/layout/objstmpconsiderwrapinfl.cxx
new file mode 100644
index 000000000..e35336b9f
--- /dev/null
+++ b/sw/source/core/layout/objstmpconsiderwrapinfl.cxx
@@ -0,0 +1,60 @@
+/* -*- 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 "objstmpconsiderwrapinfl.hxx"
+#include <anchoredobject.hxx>
+
+SwObjsMarkedAsTmpConsiderWrapInfluence::SwObjsMarkedAsTmpConsiderWrapInfluence()
+{
+}
+
+SwObjsMarkedAsTmpConsiderWrapInfluence::~SwObjsMarkedAsTmpConsiderWrapInfluence()
+{
+ Clear();
+}
+
+void SwObjsMarkedAsTmpConsiderWrapInfluence::Insert( SwAnchoredObject& _rAnchoredObj )
+{
+ auto it = std::find(maObjsTmpConsiderWrapInfl.begin(), maObjsTmpConsiderWrapInfl.end(), &_rAnchoredObj);
+ if (it != maObjsTmpConsiderWrapInfl.end())
+ return;
+ maObjsTmpConsiderWrapInfl.push_back( &_rAnchoredObj );
+}
+
+void SwObjsMarkedAsTmpConsiderWrapInfluence::Remove( SwAnchoredObject& _rAnchoredObj )
+{
+ auto it = std::find(maObjsTmpConsiderWrapInfl.begin(), maObjsTmpConsiderWrapInfl.end(), &_rAnchoredObj);
+ if (it == maObjsTmpConsiderWrapInfl.end())
+ return;
+ maObjsTmpConsiderWrapInfl.erase(it);
+}
+
+void SwObjsMarkedAsTmpConsiderWrapInfluence::Clear()
+{
+ while ( !maObjsTmpConsiderWrapInfl.empty() )
+ {
+ SwAnchoredObject* pAnchoredObj = maObjsTmpConsiderWrapInfl.back();
+ pAnchoredObj->SetTmpConsiderWrapInfluence( false );
+ pAnchoredObj->SetClearedEnvironment( false );
+
+ maObjsTmpConsiderWrapInfl.pop_back();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objstmpconsiderwrapinfl.hxx b/sw/source/core/layout/objstmpconsiderwrapinfl.hxx
new file mode 100644
index 000000000..28b6acf9d
--- /dev/null
+++ b/sw/source/core/layout/objstmpconsiderwrapinfl.hxx
@@ -0,0 +1,42 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJSTMPCONSIDERWRAPINFL_HXX
+#define INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJSTMPCONSIDERWRAPINFL_HXX
+
+#include <vector>
+
+class SwAnchoredObject;
+
+class SwObjsMarkedAsTmpConsiderWrapInfluence
+{
+ private:
+ std::vector< SwAnchoredObject* > maObjsTmpConsiderWrapInfl;
+
+ public:
+ SwObjsMarkedAsTmpConsiderWrapInfluence();
+ ~SwObjsMarkedAsTmpConsiderWrapInfluence();
+
+ void Insert( SwAnchoredObject& _rAnchoredObj );
+ void Remove( SwAnchoredObject& _rAnchoredObj );
+ void Clear();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/pagechg.cxx b/sw/source/core/layout/pagechg.cxx
new file mode 100644
index 000000000..42720ee19
--- /dev/null
+++ b/sw/source/core/layout/pagechg.cxx
@@ -0,0 +1,2614 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <comphelper/lok.hxx>
+#include <ndole.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svl/itemiter.hxx>
+#include <fmtfsize.hxx>
+#include <fmthdft.hxx>
+#include <fmtclds.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtornt.hxx>
+#include <fmtsrnd.hxx>
+#include <ftninfo.hxx>
+#include <frmtool.hxx>
+#include <tgrditem.hxx>
+#include <viewopt.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <view.hxx>
+#include <edtwin.hxx>
+#include <frameformats.hxx>
+
+#include <viewimp.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <dcontact.hxx>
+#include <hints.hxx>
+#include <FrameControlsManager.hxx>
+
+#include <ftnidx.hxx>
+#include <bodyfrm.hxx>
+#include <ftnfrm.hxx>
+#include <tabfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <layact.hxx>
+#include <flyfrms.hxx>
+#include <htmltbl.hxx>
+#include <pagedesc.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <sortedobjs.hxx>
+#include <calbck.hxx>
+#include <txtfly.hxx>
+
+using namespace ::com::sun::star;
+
+SwBodyFrame::SwBodyFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
+ SwLayoutFrame( pFormat, pSib )
+{
+ mnFrameType = SwFrameType::Body;
+}
+
+void SwBodyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
+{
+ // Formatting of the body is too simple, thus, it gets its own format method.
+ // Borders etc. are not taken into account here.
+ // Width is taken from the PrtArea of the Upper. Height is the height of the
+ // PrtArea of the Upper minus any neighbors (for robustness).
+ // The PrtArea has always the size of the frame.
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ SwTwips nHeight = GetUpper()->getFramePrintArea().Height();
+ SwTwips nWidth = GetUpper()->getFramePrintArea().Width();
+ const SwFrame *pFrame = GetUpper()->Lower();
+ do
+ {
+ if ( pFrame != this )
+ {
+ if( pFrame->IsVertical() )
+ nWidth -= pFrame->getFrameArea().Width();
+ else
+ nHeight -= pFrame->getFrameArea().Height();
+ }
+ pFrame = pFrame->GetNext();
+ } while ( pFrame );
+
+ if ( nHeight < 0 )
+ {
+ nHeight = 0;
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( nHeight );
+
+ if( IsVertical() && !IsVertLR() && nWidth != aFrm.Width() )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() + aFrm.Width() - nWidth);
+ }
+
+ aFrm.Width( nWidth );
+ }
+
+ bool bNoGrid = true;
+ if( GetUpper()->IsPageFrame() && static_cast<SwPageFrame*>(GetUpper())->HasGrid() )
+ {
+ SwTextGridItem const*const pGrid(
+ GetGridItem(static_cast<SwPageFrame*>(GetUpper())));
+ if( pGrid )
+ {
+ bNoGrid = false;
+ tools::Long nSum = pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nSize = aRectFnSet.GetWidth(getFrameArea());
+ tools::Long nBorder = 0;
+ if( GRID_LINES_CHARS == pGrid->GetGridType() )
+ {
+ //for textgrid refactor
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ nBorder = nSize % (GetGridWidth(*pGrid, *pDoc));
+ nSize -= nBorder;
+ nBorder /= 2;
+ }
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetPosX( aPrt, nBorder );
+ aRectFnSet.SetWidth( aPrt, nSize );
+
+ // Height of body frame:
+ nBorder = aRectFnSet.GetHeight(getFrameArea());
+
+ // Number of possible lines in area of body frame:
+ tools::Long nNumberOfLines = nBorder / nSum;
+ if( nNumberOfLines > pGrid->GetLines() )
+ nNumberOfLines = pGrid->GetLines();
+
+ // Space required for nNumberOfLines lines:
+ nSize = nNumberOfLines * nSum;
+ nBorder -= nSize;
+ nBorder /= 2;
+
+ // #i21774# Footnotes and centering the grid does not work together:
+ const bool bAdjust = static_cast<SwPageFrame*>(GetUpper())->GetFormat()->GetDoc()->
+ GetFootnoteIdxs().empty();
+
+ aRectFnSet.SetPosY( aPrt, bAdjust ? nBorder : 0 );
+ aRectFnSet.SetHeight( aPrt, nSize );
+ }
+ }
+
+ if( bNoGrid )
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Pos().setX(0);
+ aPrt.Pos().setY(0);
+ aPrt.Height( getFrameArea().Height() );
+ aPrt.Width( getFrameArea().Width() );
+ }
+
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+}
+
+SwPageFrame::SwPageFrame( SwFrameFormat *pFormat, SwFrame* pSib, SwPageDesc *pPgDsc ) :
+ SwFootnoteBossFrame( pFormat, pSib ),
+ m_pDesc( pPgDsc ),
+ m_nPhyPageNum( 0 )
+{
+ SetDerivedVert( false );
+ SetDerivedR2L( false );
+ if( m_pDesc )
+ {
+ m_bHasGrid = true;
+ SwTextGridItem const*const pGrid(GetGridItem(this));
+ if( !pGrid )
+ m_bHasGrid = false;
+ }
+ else
+ m_bHasGrid = false;
+ SetMaxFootnoteHeight( pPgDsc->GetFootnoteInfo().GetHeight() ?
+ pPgDsc->GetFootnoteInfo().GetHeight() : LONG_MAX );
+ mnFrameType = SwFrameType::Page;
+ m_bInvalidLayout = m_bInvalidContent = m_bInvalidSpelling = m_bInvalidSmartTags = m_bInvalidAutoCmplWrds = m_bInvalidWordCount = true;
+ m_bInvalidFlyLayout = m_bInvalidFlyContent = m_bInvalidFlyInCnt = m_bFootnotePage = m_bEndNotePage = false;
+
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if ( bBrowseMode )
+ {
+ aFrm.Height( 0 );
+ tools::Long nWidth = pSh->VisArea().Width();
+
+ if ( !nWidth )
+ {
+ nWidth = 5000; // changes anyway
+ }
+
+ aFrm.Width ( nWidth );
+ }
+ else
+ {
+ aFrm.SSize( pFormat->GetFrameSize().GetSize() );
+ }
+ }
+
+ // create and insert body area if it is not a blank page
+ SwDoc* pDoc(pFormat->GetDoc());
+ m_bEmptyPage = (pFormat == pDoc->GetEmptyPageFormat());
+
+ if(m_bEmptyPage)
+ {
+ return;
+ }
+
+ Calc(pRenderContext); // so that the PrtArea is correct
+ SwBodyFrame *pBodyFrame = new SwBodyFrame( pDoc->GetDfltFrameFormat(), this );
+ pBodyFrame->ChgSize( getFramePrintArea().SSize() );
+ pBodyFrame->Paste( this );
+ pBodyFrame->Calc(pRenderContext); // so that the columns can be inserted correctly
+ pBodyFrame->InvalidatePos();
+
+ if ( bBrowseMode )
+ InvalidateSize_();
+
+ // insert header/footer,, but only if active.
+ if ( pFormat->GetHeader().IsActive() )
+ PrepareHeader();
+ if ( pFormat->GetFooter().IsActive() )
+ PrepareFooter();
+
+ const SwFormatCol &rCol = pFormat->GetCol();
+ if ( rCol.GetNumCols() > 1 )
+ {
+ const SwFormatCol aOld; //ChgColumns() needs an old value
+ pBodyFrame->ChgColumns( aOld, rCol );
+ }
+
+}
+
+void SwPageFrame::DestroyImpl()
+{
+ // Cleanup the header-footer controls in all SwEditWins
+ SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if (pSh)
+ {
+ for (SwViewShell& rSh : pSh->GetRingContainer())
+ {
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( &rSh );
+ if ( pWrtSh )
+ {
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ rEditWin.GetFrameControlsManager( ).RemoveControls( this );
+ }
+ }
+ }
+
+ // empty FlyContainer, deletion of the Flys is done by the anchor (in base class SwFrame)
+ if (m_pSortedObjs)
+ {
+ // Objects can be anchored at pages that are before their anchors (why ever...).
+ // In such cases, we would access already freed memory.
+ for (SwAnchoredObject* pAnchoredObj : *m_pSortedObjs)
+ {
+ pAnchoredObj->SetPageFrame( nullptr );
+ }
+ m_pSortedObjs.reset(); // reset to zero to prevent problems when detaching the Flys
+ }
+
+ // prevent access to destroyed pages
+ SwDoc *pDoc = GetFormat() ? GetFormat()->GetDoc() : nullptr;
+ if( pDoc && !pDoc->IsInDtor() )
+ {
+ if ( pSh )
+ {
+ SwViewShellImp *pImp = pSh->Imp();
+ pImp->SetFirstVisPageInvalid();
+ if ( pImp->IsAction() )
+ pImp->GetLayAction().SetAgain(true);
+ // #i9719# - retouche area of page
+ // including border and shadow area.
+ const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
+ SwRect aRetoucheRect;
+ SwPageFrame::GetBorderAndShadowBoundRect( getFrameArea(), pSh, pSh->GetOut(), aRetoucheRect, IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
+ pSh->AddPaintRect( aRetoucheRect );
+ }
+ }
+
+ SwFootnoteBossFrame::DestroyImpl();
+}
+
+SwPageFrame::~SwPageFrame()
+{
+}
+
+void SwPageFrame::CheckGrid( bool bInvalidate )
+{
+ bool bOld = m_bHasGrid;
+ m_bHasGrid = true;
+ SwTextGridItem const*const pGrid(GetGridItem(this));
+ m_bHasGrid = nullptr != pGrid;
+ if( !(bInvalidate || bOld != m_bHasGrid) )
+ return;
+
+ SwLayoutFrame* pBody = FindBodyCont();
+ if( pBody )
+ {
+ pBody->InvalidatePrt();
+ SwContentFrame* pFrame = pBody->ContainsContent();
+ while( pBody->IsAnLower( pFrame ) )
+ {
+ static_cast<SwTextFrame*>(pFrame)->Prepare();
+ pFrame = pFrame->GetNextContentFrame();
+ }
+ }
+ SetCompletePaint();
+}
+
+void SwPageFrame::CheckDirection( bool bVert )
+{
+ SvxFrameDirection nDir = GetFormat()->GetFormatAttr( RES_FRAMEDIR ).GetValue();
+ if( bVert )
+ {
+ if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir )
+ {
+ mbVertLR = false;
+ mbVertical = false;
+ }
+ else
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ mbVertLR = false;
+ mbVertical = false;
+ }
+ else
+ {
+ mbVertical = true;
+
+ if(SvxFrameDirection::Vertical_RL_TB == nDir)
+ mbVertLR = false;
+ else if(SvxFrameDirection::Vertical_LR_TB==nDir)
+ mbVertLR = true;
+ }
+ }
+
+ mbInvalidVert = false;
+ }
+ else
+ {
+ if( SvxFrameDirection::Horizontal_RL_TB == nDir )
+ mbRightToLeft = true;
+ else
+ mbRightToLeft = false;
+ mbInvalidR2L = false;
+ }
+}
+
+/// create specific Flys for this page and format generic content
+static void lcl_FormatLay( SwLayoutFrame *pLay )
+{
+ vcl::RenderContext* pRenderContext = pLay->getRootFrame()->GetCurrShell()->GetOut();
+ // format all LayoutFrames - no tables, Flys etc.
+
+ SwFrame *pTmp = pLay->Lower();
+ // first the low-level ones
+ while ( pTmp )
+ {
+ const SwFrameType nTypes = SwFrameType::Root | SwFrameType::Page | SwFrameType::Column
+ | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont
+ | SwFrameType::Ftn | SwFrameType::Body;
+ if ( pTmp->GetType() & nTypes )
+ ::lcl_FormatLay( static_cast<SwLayoutFrame*>(pTmp) );
+ pTmp = pTmp->GetNext();
+ }
+ pLay->Calc(pRenderContext);
+}
+
+/// Create Flys or register draw objects
+static void lcl_MakeObjs( const SwFrameFormats &rTable, SwPageFrame *pPage )
+{
+ // formats are in the special table of the document
+
+ for ( size_t i = 0; i < rTable.size(); ++i )
+ {
+ SwFrameFormat *pFormat = rTable[i];
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ if ( rAnch.GetPageNum() == pPage->GetPhyPageNum() )
+ {
+ if( rAnch.GetContentAnchor() )
+ {
+ if (RndStdIds::FLY_AT_PAGE == rAnch.GetAnchorId())
+ {
+ SwFormatAnchor aAnch( rAnch );
+ aAnch.SetAnchor( nullptr );
+ pFormat->SetFormatAttr( aAnch );
+ }
+ else
+ continue;
+ }
+
+ // is it a border or a SdrObject?
+ bool bSdrObj = RES_DRAWFRMFMT == pFormat->Which();
+ SdrObject *pSdrObj = nullptr;
+ if ( bSdrObj && nullptr == (pSdrObj = pFormat->FindSdrObject()) )
+ {
+ OSL_FAIL( "DrawObject not found." );
+ pFormat->GetDoc()->DelFrameFormat( pFormat );
+ --i;
+ continue;
+ }
+ // The object might be anchored to another page, e.g. when inserting
+ // a new page due to a page descriptor change. In such cases, the
+ // object needs to be moved.
+ // In some cases the object is already anchored to the correct page.
+ // This will be handled here and does not need to be coded extra.
+ SwPageFrame *pPg = pPage->IsEmptyPage() ? static_cast<SwPageFrame*>(pPage->GetNext()) : pPage;
+ if ( bSdrObj )
+ {
+ // OD 23.06.2003 #108784# - consider 'virtual' drawing objects
+ SwDrawContact *pContact =
+ static_cast<SwDrawContact*>(::GetUserCall(pSdrObj));
+ if ( auto pDrawVirtObj = dynamic_cast<SwDrawVirtObj *>( pSdrObj ) )
+ {
+ if ( pContact )
+ {
+ pDrawVirtObj->RemoveFromWriterLayout();
+ pDrawVirtObj->RemoveFromDrawingPage();
+ pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pDrawVirtObj )) );
+ }
+ }
+ else
+ {
+ if ( pContact->GetAnchorFrame() )
+ pContact->DisconnectFromLayout( false );
+ pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pSdrObj )) );
+ }
+ }
+ else
+ {
+ SwIterator<SwFlyFrame,SwFormat> aIter( *pFormat );
+ SwFlyFrame *pFly = aIter.First();
+ if ( pFly)
+ {
+ if( pFly->GetAnchorFrame() )
+ pFly->AnchorFrame()->RemoveFly( pFly );
+ }
+ else
+ pFly = new SwFlyLayFrame( static_cast<SwFlyFrameFormat*>(pFormat), pPg, pPg );
+ pPg->AppendFly( pFly );
+ ::RegistFlys( pPg, pFly );
+ }
+ }
+ }
+}
+
+void SwPageFrame::PreparePage( bool bFootnote )
+{
+ SetFootnotePage( bFootnote );
+
+ // #i82258#
+ // Due to made change on OOo 2.0 code line, method <::lcl_FormatLay(..)> has
+ // the side effect, that the content of page header and footer are formatted.
+ // For this formatting it is needed that the anchored objects are registered
+ // at the <SwPageFrame> instance.
+ // Thus, first calling <::RegistFlys(..)>, then call <::lcl_FormatLay(..)>
+ ::RegistFlys( this, this );
+
+ if ( Lower() )
+ {
+ ::lcl_FormatLay( this );
+ }
+
+ // Flys and draw objects that are still attached to the document.
+ // Footnote pages do not have page-bound Flys!
+ // There might be Flys or draw objects that want to be placed on
+ // empty pages, however, the empty pages ignore that and the following
+ // pages take care of them.
+ if ( !bFootnote && !IsEmptyPage() )
+ {
+ SwDoc *pDoc = GetFormat()->GetDoc();
+
+ if ( GetPrev() && static_cast<SwPageFrame*>(GetPrev())->IsEmptyPage() )
+ lcl_MakeObjs( *pDoc->GetSpzFrameFormats(), static_cast<SwPageFrame*>(GetPrev()) );
+ lcl_MakeObjs( *pDoc->GetSpzFrameFormats(), this );
+ }
+}
+
+void SwPageFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if(typeid(sw::PageFootnoteHint) == typeid(rHint))
+ {
+ // currently the savest way:
+ static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
+ SetMaxFootnoteHeight(m_pDesc->GetFootnoteInfo().GetHeight());
+ if(!GetMaxFootnoteHeight())
+ SetMaxFootnoteHeight(LONG_MAX);
+ SetColMaxFootnoteHeight();
+ // here, the page might be destroyed:
+ static_cast<SwRootFrame*>(GetUpper())->RemoveFootnotes(nullptr, false, true);
+ }
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(auto pSh = getRootFrame()->GetCurrShell())
+ pSh->SetFirstVisPageInvalid();
+
+ SwPageFrameInvFlags eInvFlags = SwPageFrameInvFlags::NONE;
+ if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
+ {
+ auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ SfxItemIter aOIter(*rOldSetChg.GetChgSet());
+ SfxItemIter aNIter(*rNewSetChg.GetChgSet());
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ SwAttrSetChg aOldSet(rOldSetChg);
+ SwAttrSetChg aNewSet(rNewSetChg);
+ do
+ {
+ UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
+ pOItem = aOIter.NextItem();
+ pNItem = aNIter.NextItem();
+ } while(pNItem);
+ if(aOldSet.Count() || aNewSet.Count())
+ SwLayoutFrame::SwClientNotify(rModify, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ else
+ UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if (eInvFlags == SwPageFrameInvFlags::NONE)
+ return;
+
+ InvalidatePage( this );
+ if(eInvFlags & SwPageFrameInvFlags::InvalidatePrt)
+ InvalidatePrt_();
+ if(eInvFlags & SwPageFrameInvFlags::SetCompletePaint)
+ SetCompletePaint();
+ if(eInvFlags & SwPageFrameInvFlags::InvalidateNextPos && GetNext() )
+ GetNext()->InvalidatePos();
+ if(eInvFlags & SwPageFrameInvFlags::PrepareHeader)
+ PrepareHeader();
+ if(eInvFlags & SwPageFrameInvFlags::PrepareFooter)
+ PrepareFooter();
+ if(eInvFlags & SwPageFrameInvFlags::CheckGrid)
+ CheckGrid(bool(eInvFlags & SwPageFrameInvFlags::InvalidateGrid));
+ } else
+ SwFrame::SwClientNotify(rModify, rHint);
+}
+
+void SwPageFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
+ SwPageFrameInvFlags &rInvFlags,
+ SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
+{
+ bool bClear = true;
+ const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ switch( nWhich )
+ {
+ case RES_FMT_CHG:
+ {
+ // state of m_bEmptyPage needs to be determined newly
+ const bool bNewState(GetFormat() == GetFormat()->GetDoc()->GetEmptyPageFormat());
+
+ if(m_bEmptyPage != bNewState)
+ {
+ // copy new state
+ m_bEmptyPage = bNewState;
+
+ if(nullptr == GetLower())
+ {
+ // if we were an empty page before there is not yet a BodyArea in the
+ // form of a SwBodyFrame, see constructor
+ SwViewShell* pSh(getRootFrame()->GetCurrShell());
+ vcl::RenderContext* pRenderContext(pSh ? pSh->GetOut() : nullptr);
+ Calc(pRenderContext); // so that the PrtArea is correct
+ SwBodyFrame* pBodyFrame = new SwBodyFrame(GetFormat(), this);
+ pBodyFrame->ChgSize(getFramePrintArea().SSize());
+ pBodyFrame->Paste(this);
+ pBodyFrame->InvalidatePos();
+ }
+ }
+
+ // If the frame format is changed, several things might also change:
+ // 1. columns:
+ assert(pOld && pNew); //FMT_CHG Missing Format
+ const SwFormat *const pOldFormat = static_cast<const SwFormatChg*>(pOld)->pChangedFormat;
+ const SwFormat *const pNewFormat = static_cast<const SwFormatChg*>(pNew)->pChangedFormat;
+ assert(pOldFormat && pNewFormat); //FMT_CHG Missing Format
+ const SwFormatCol &rOldCol = pOldFormat->GetCol();
+ const SwFormatCol &rNewCol = pNewFormat->GetCol();
+ if( rOldCol != rNewCol )
+ {
+ SwLayoutFrame *pB = FindBodyCont();
+ assert(pB && "Page without Body.");
+ pB->ChgColumns( rOldCol, rNewCol );
+ rInvFlags |= SwPageFrameInvFlags::CheckGrid;
+ }
+
+ // 2. header and footer:
+ const SwFormatHeader &rOldH = pOldFormat->GetHeader();
+ const SwFormatHeader &rNewH = pNewFormat->GetHeader();
+ if( rOldH != rNewH )
+ rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
+
+ const SwFormatFooter &rOldF = pOldFormat->GetFooter();
+ const SwFormatFooter &rNewF = pNewFormat->GetFooter();
+ if( rOldF != rNewF )
+ rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
+ CheckDirChange();
+
+ [[fallthrough]];
+ }
+ case RES_FRM_SIZE:
+ {
+ const SwRect aOldPageFrameRect( getFrameArea() );
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ setFrameAreaSizeValid(false);
+ // OD 28.10.2002 #97265# - Don't call <SwPageFrame::MakeAll()>
+ // Calculation of the page is not necessary, because its size is
+ // invalidated here and further invalidation is done in the
+ // calling method <SwPageFrame::Modify(..)> and probably by calling
+ // <SwLayoutFrame::SwClientNotify(..)> at the end.
+ // It can also causes inconsistences, because the lowers are
+ // adjusted, but not calculated, and a <SwPageFrame::MakeAll()> of
+ // a next page is called. This is performed on the switch to the
+ // online layout.
+ //MakeAll();
+ }
+ else if (pNew)
+ {
+ const SwFormatFrameSize &rSz = nWhich == RES_FMT_CHG ?
+ static_cast<const SwFormatChg*>(pNew)->pChangedFormat->GetFrameSize() :
+ static_cast<const SwFormatFrameSize&>(*pNew);
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( std::max( rSz.GetHeight(), tools::Long(MINLAY) ) );
+ aFrm.Width ( std::max( rSz.GetWidth(), tools::Long(MINLAY) ) );
+ }
+
+ if ( GetUpper() )
+ {
+ static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
+ }
+ }
+ // cleanup Window
+ if( pSh && pSh->GetWin() && aOldPageFrameRect.HasArea() )
+ {
+ // #i9719# - consider border and shadow of
+ // page frame for determine 'old' rectangle - it's used for invalidating.
+ const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
+ SwRect aOldRectWithBorderAndShadow;
+ SwPageFrame::GetBorderAndShadowBoundRect( aOldPageFrameRect, pSh, pSh->GetOut(), aOldRectWithBorderAndShadow,
+ IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
+ pSh->InvalidateWindows( aOldRectWithBorderAndShadow );
+ }
+ rInvFlags |= SwPageFrameInvFlags::InvalidatePrt | SwPageFrameInvFlags::SetCompletePaint;
+ if ( aOldPageFrameRect.Height() != getFrameArea().Height() )
+ rInvFlags |= SwPageFrameInvFlags::InvalidateNextPos;
+ }
+ break;
+
+ case RES_COL:
+ assert(pOld && pNew); //COL Missing Format
+ if (pOld && pNew)
+ {
+ SwLayoutFrame *pB = FindBodyCont();
+ assert(pB); //page without body
+ pB->ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
+ rInvFlags |= SwPageFrameInvFlags::SetCompletePaint | SwPageFrameInvFlags::CheckGrid;
+ }
+ break;
+
+ case RES_HEADER:
+ rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
+ break;
+
+ case RES_FOOTER:
+ rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
+ break;
+ case RES_TEXTGRID:
+ rInvFlags |= SwPageFrameInvFlags::CheckGrid | SwPageFrameInvFlags::InvalidateGrid;
+ break;
+ case RES_FRAMEDIR :
+ CheckDirChange();
+ break;
+
+ default:
+ bClear = false;
+ }
+ if ( !bClear )
+ return;
+
+ if ( pOldSet || pNewSet )
+ {
+ if ( pOldSet )
+ pOldSet->ClearItem( nWhich );
+ if ( pNewSet )
+ pNewSet->ClearItem( nWhich );
+ }
+ else
+ {
+ SwModify aMod;
+ SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ }
+}
+
+/// get information from Modify
+bool SwPageFrame::GetInfo( SfxPoolItem & rInfo ) const
+{
+ if( RES_AUTOFMT_DOCNODE == rInfo.Which() )
+ {
+ // a page frame exists, so use this one
+ return false;
+ }
+ return true; // continue searching
+}
+
+void SwPageFrame::SetPageDesc( SwPageDesc *pNew, SwFrameFormat *pFormat )
+{
+ m_pDesc = pNew;
+ if ( pFormat )
+ SetFrameFormat( pFormat );
+}
+
+/* determine the right PageDesc:
+ * 0. from the document for footnote and endnote pages
+ * 1. from the first BodyContent below a page
+ * 2. from PageDesc of the predecessor page
+ * 3. from PageDesc of the previous page if blank page
+ * 3.1 from PageDesc of the next page if no predecessor exists
+ * 4. default PageDesc
+ * 5. In BrowseMode use the first paragraph or default PageDesc.
+ */
+SwPageDesc *SwPageFrame::FindPageDesc()
+{
+ // 0.
+ if ( IsFootnotePage() )
+ {
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ if ( IsEndNotePage() )
+ return pDoc->GetEndNoteInfo().GetPageDesc( *pDoc );
+ else
+ return pDoc->GetFootnoteInfo().GetPageDesc( *pDoc );
+ }
+
+ SwPageDesc *pRet = nullptr;
+
+ //5.
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ SwContentFrame *pFrame = GetUpper()->ContainsContent();
+ while (pFrame && !pFrame->IsInDocBody())
+ pFrame = pFrame->GetNextContentFrame();
+ if (pFrame)
+ {
+ SwFrame *pFlow = pFrame;
+ if ( pFlow->IsInTab() )
+ pFlow = pFlow->FindTabFrame();
+ pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
+ }
+ if ( !pRet )
+ pRet = &GetFormat()->GetDoc()->GetPageDesc( 0 );
+ return pRet;
+ }
+
+ SwFrame *pFlow = FindFirstBodyContent();
+ if ( pFlow && pFlow->IsInTab() )
+ pFlow = pFlow->FindTabFrame();
+
+ //1.
+ if ( pFlow )
+ {
+ SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
+ if ( !pTmp->IsFollow() )
+ pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
+ }
+
+ //3. and 3.1
+ if ( !pRet && IsEmptyPage() )
+ // FME 2008-03-03 #i81544# lijian/fme: an empty page should have
+ // the same page description as its prev, just like after construction
+ // of the empty page.
+ pRet = GetPrev() ? static_cast<SwPageFrame*>(GetPrev())->GetPageDesc() :
+ GetNext() ? static_cast<SwPageFrame*>(GetNext())->GetPageDesc() : nullptr;
+
+ //2.
+ if ( !pRet )
+ pRet = GetPrev() ?
+ static_cast<SwPageFrame*>(GetPrev())->GetPageDesc()->GetFollow() : nullptr;
+
+ //4.
+ if ( !pRet )
+ pRet = &GetFormat()->GetDoc()->GetPageDesc( 0 );
+
+ OSL_ENSURE( pRet, "could not find page descriptor." );
+ return pRet;
+}
+
+// Notify if the RootFrame changes its size
+void AdjustSizeChgNotify( SwRootFrame *pRoot )
+{
+ const bool bOld = pRoot->IsSuperfluous();
+ pRoot->mbCheckSuperfluous = false;
+ if ( pRoot->GetCurrShell() )
+ {
+ for(SwViewShell& rSh : pRoot->GetCurrShell()->GetRingContainer())
+ {
+ if( pRoot == rSh.GetLayout() )
+ {
+ rSh.SizeChgNotify();
+ if ( rSh.Imp() )
+ rSh.Imp()->NotifySizeChg( pRoot->getFrameArea().SSize() );
+ }
+ }
+ }
+ pRoot->mbCheckSuperfluous = bOld;
+}
+
+inline void SetLastPage( SwPageFrame *pPage )
+{
+ static_cast<SwRootFrame*>(pPage->GetUpper())->mpLastPage = pPage;
+}
+
+void SwPageFrame::Cut()
+{
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( !IsEmptyPage() )
+ {
+ if ( GetNext() )
+ GetNext()->InvalidatePos();
+
+ // move Flys whose anchor is on a different page (draw objects are not relevant here)
+ if ( GetSortedObjs() )
+ {
+ size_t i = 0;
+ while ( GetSortedObjs() && i < GetSortedObjs()->size() )
+ {
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = (*GetSortedObjs())[i];
+
+ if ( auto pFly = dynamic_cast<SwFlyAtContentFrame *>( pAnchoredObj ) )
+ {
+ SwPageFrame *pAnchPage = pFly->GetAnchorFrame() ?
+ pFly->AnchorFrame()->FindPageFrame() : nullptr;
+ if ( pAnchPage && (pAnchPage != this) )
+ {
+ MoveFly( pFly, pAnchPage );
+ pFly->InvalidateSize();
+ pFly->InvalidatePos_();
+ // Do not increment index, in this case
+ continue;
+ }
+ }
+ ++i;
+ }
+ }
+ // cleanup Window
+ if ( pSh && pSh->GetWin() )
+ pSh->InvalidateWindows( getFrameArea() );
+ }
+
+ // decrease the root's page number
+ static_cast<SwRootFrame*>(GetUpper())->DecrPhyPageNums();
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
+ if ( pPg )
+ {
+ while ( pPg )
+ {
+ --pPg->m_nPhyPageNum;
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ }
+ }
+ else
+ ::SetLastPage( static_cast<SwPageFrame*>(GetPrev()) );
+
+ SwFrame* pRootFrame = GetUpper();
+
+ // cut all connections
+ RemoveFromLayout();
+
+ if ( pRootFrame )
+ static_cast<SwRootFrame*>(pRootFrame)->CheckViewLayout( nullptr, nullptr );
+}
+
+void SwPageFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
+{
+ OSL_ENSURE( pParent->IsRootFrame(), "Parent is no Root." );
+ OSL_ENSURE( pParent, "No parent for Paste()." );
+ OSL_ENSURE( pParent != this, "I'm my own parent." );
+ OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
+ OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
+ "I am still registered somewhere." );
+
+ // insert into tree structure
+ InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
+
+ // increase the root's page number
+ static_cast<SwRootFrame*>(GetUpper())->IncrPhyPageNums();
+ if( GetPrev() )
+ SetPhyPageNum( static_cast<SwPageFrame*>(GetPrev())->GetPhyPageNum() + 1 );
+ else
+ SetPhyPageNum( 1 );
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
+ if ( pPg )
+ {
+ while ( pPg )
+ {
+ ++pPg->m_nPhyPageNum;
+ pPg->InvalidatePos_();
+ pPg->InvalidateLayout();
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ }
+ }
+ else
+ ::SetLastPage( this );
+
+ if( getFrameArea().Width() != pParent->getFramePrintArea().Width() )
+ InvalidateSize_();
+
+ InvalidatePos();
+
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( pSh )
+ pSh->SetFirstVisPageInvalid();
+
+ getRootFrame()->CheckViewLayout( nullptr, nullptr );
+}
+
+static void lcl_PrepFlyInCntRegister( SwContentFrame *pFrame )
+{
+ pFrame->Prepare( PrepareHint::Register );
+ if( !pFrame->GetDrawObjs() )
+ return;
+
+ for(SwAnchoredObject* pAnchoredObj : *pFrame->GetDrawObjs())
+ {
+ // #i28701#
+ if ( auto pFly = dynamic_cast<SwFlyInContentFrame *>( pAnchoredObj ) )
+ {
+ SwContentFrame *pCnt = pFly->ContainsContent();
+ while ( pCnt )
+ {
+ lcl_PrepFlyInCntRegister( pCnt );
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ }
+ }
+}
+
+void SwPageFrame::PrepareRegisterChg()
+{
+ SwContentFrame *pFrame = FindFirstBodyContent();
+ while( pFrame )
+ {
+ lcl_PrepFlyInCntRegister( pFrame );
+ pFrame = pFrame->GetNextContentFrame();
+ if( !IsAnLower( pFrame ) )
+ break;
+ }
+ if( !GetSortedObjs() )
+ return;
+
+ for(SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
+ {
+ // #i28701#
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ pFrame = pFly->ContainsContent();
+ while ( pFrame )
+ {
+ ::lcl_PrepFlyInCntRegister( pFrame );
+ pFrame = pFrame->GetNextContentFrame();
+ }
+ }
+ }
+}
+
+namespace sw {
+
+/// check if there's content on the page that requires it to exist
+bool IsPageFrameEmpty(SwPageFrame const& rPage)
+{
+ bool bExistEssentialObjs = (nullptr != rPage.GetSortedObjs());
+ if (bExistEssentialObjs)
+ {
+ // Only because the page has Flys does not mean that it is needed. If all Flys are
+ // attached to generic content it is also superfluous (checking DocBody should be enough)
+ // OD 19.06.2003 - consider that drawing objects in
+ // header/footer are supported now.
+ bool bOnlySuperfluousObjs = true;
+ SwSortedObjs const& rObjs = *rPage.GetSortedObjs();
+ for (size_t i = 0; bOnlySuperfluousObjs && i < rObjs.size(); ++i)
+ {
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = rObjs[i];
+ // do not consider hidden objects
+ if ( rPage.GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
+ pAnchoredObj->GetDrawObj()->GetLayer() ) &&
+ !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() )
+ {
+ bOnlySuperfluousObjs = false;
+ }
+ }
+ bExistEssentialObjs = !bOnlySuperfluousObjs;
+ }
+
+ // optimization: check first if essential objects exist.
+ const SwLayoutFrame* pBody = nullptr;
+ if ( bExistEssentialObjs ||
+ rPage.FindFootnoteCont() ||
+ (nullptr != (pBody = rPage.FindBodyCont()) &&
+ ( pBody->ContainsContent() ||
+ // check for section frames that are being formatted on the stack
+ rPage.ContainsDeleteForbiddenLayFrame() ||
+ // #i47580#
+ // Do not delete page if there's an empty tabframe
+ // left. I think it might be correct to use ContainsAny()
+ // instead of ContainsContent() to cover the empty-table-case,
+ // but I'm not fully sure, since ContainsAny() also returns
+ // SectionFrames. Therefore I prefer to do it the safe way:
+ ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) )
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+} // namespace sw
+
+//FIXME: provide missing documentation
+/** Check all pages (starting from the given one) if they use the appropriate frame format.
+ *
+ * If "wrong" pages are found, try to fix this as simple as possible.
+ *
+ * Also delete pages that don't have content on them.
+ *
+ * @param pStart the page from where to start searching
+ * @param bNotifyFields
+ * @param ppPrev
+ */
+void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFrame** ppPrev )
+{
+ SAL_INFO( "sw.pageframe", "(CheckPageDescs in phy: " << pStart->GetPhyPageNum() );
+ assert(pStart && "no starting page.");
+
+ SwViewShell *pSh = pStart->getRootFrame()->GetCurrShell();
+ SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
+
+ if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
+ {
+ pImp->GetLayAction().SetCheckPageNum( pStart->GetPhyPageNum() );
+ SAL_INFO( "sw.pageframe", "CheckPageDescs out fast - via SetCheckPageNum: "
+ << pStart->GetPhyPageNum() << ")" );
+ return;
+ }
+
+ // For the update of page numbering fields, nDocPos provides
+ // the page position from where invalidation should start.
+ SwTwips nDocPos = LONG_MAX;
+
+ SwRootFrame *pRoot = static_cast<SwRootFrame*>(pStart->GetUpper());
+ SwDoc* pDoc = pStart->GetFormat()->GetDoc();
+ const bool bFootnotes = !pDoc->GetFootnoteIdxs().empty();
+
+ SwPageFrame *pPage = pStart;
+ if( pPage->GetPrev() && static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
+ while ( pPage )
+ {
+ SwPageFrame *pPrevPage = static_cast<SwPageFrame*>(pPage->GetPrev());
+ SwPageFrame *pNextPage = static_cast<SwPageFrame*>(pPage->GetNext());
+
+ SwPageDesc *pDesc = pPage->FindPageDesc();
+ /// page is intentionally empty page
+ bool bIsEmpty = pPage->IsEmptyPage();
+ // false for intentionally empty pages, they need additional check
+ bool isPageFrameEmpty(!bIsEmpty && sw::IsPageFrameEmpty(*pPage));
+ bool bIsOdd = pPage->OnRightPage();
+ bool bWantOdd = pPage->WannaRightPage();
+ bool bFirst = pPage->OnFirstPage();
+ SwFrameFormat *pFormatWish = bWantOdd
+ ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst);
+
+ if ( bIsOdd != bWantOdd ||
+ pDesc != pPage->GetPageDesc() || // wrong Desc
+ ( pFormatWish != pPage->GetFormat() && // wrong format and
+ ( !bIsEmpty || pFormatWish ) // not blank /empty
+ )
+ )
+ {
+ // Updating a page might take a while, so check the WaitCursor
+ if( pImp )
+ pImp->CheckWaitCursor();
+
+ // invalidate the field, starting from here
+ if ( nDocPos == LONG_MAX )
+ nDocPos = pPrevPage ? pPrevPage->getFrameArea().Top() : pPage->getFrameArea().Top();
+
+ // Cases:
+ // 1. Empty page should be "normal" page -> remove empty page and take next one
+ // 2. Empty page should have different descriptor -> change
+ // 3. Normal page should be empty -> insert empty page if previous page
+ // is not empty, otherwise see (6).
+ // 4. Normal page should have different descriptor -> change
+ // 5. Normal page should have different format -> change
+ // 6. No "wish" format provided -> take the "other" format (left/right) of the PageDesc
+
+ if ( bIsEmpty && ( pFormatWish || //1.
+ ( !bWantOdd && !pPrevPage ) ) )
+ {
+ // Check all cases for the next page, so we don't oscillate empty pages
+ // Skip case 1 and 2, as we require a non-empty next page to save the empty page
+ // Case 3 is the one we actually want to predict and skip
+ // We can skip the empty check of case 3, as we just work on an existing next page
+ bool bNextWantOdd;
+ SwPageDesc *pNextDesc;
+ if ( pNextPage && !pNextPage->IsEmptyPage() && //3.
+ pNextPage->OnRightPage() == (bNextWantOdd = pNextPage->WannaRightPage()) &&
+ pNextPage->GetPageDesc() == (pNextDesc = pNextPage->FindPageDesc()) ) //4.
+ {
+ bool bNextFirst = pNextPage->OnFirstPage();
+ SwFrameFormat *pNextFormatWish = bNextWantOdd ? //5.
+ pNextDesc->GetRightFormat(bNextFirst) : pNextDesc->GetLeftFormat(bNextFirst);
+ if ( !pNextFormatWish ) // 6.
+ pNextFormatWish = bNextWantOdd ? pNextDesc->GetLeftFormat() : pNextDesc->GetRightFormat();
+ if ( pNextFormatWish && pNextPage->GetFormat() == pNextFormatWish )
+ {
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 1+3 - skip next page of p: " << pPage );
+ if (pPrevPage && pPage->GetPageDesc() != pPrevPage->GetPageDesc())
+ pPage->SetPageDesc( pPrevPage->GetPageDesc(), nullptr );
+ // We can skip the next page, as all checks were already done!
+ pPage = static_cast<SwPageFrame*>(pNextPage->GetNext());
+ continue;
+ }
+ }
+
+ pPage->Cut();
+ bool bUpdatePrev = false;
+ if (ppPrev && *ppPrev == pPage)
+ bUpdatePrev = true;
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 1 - destroy p: " << pPage );
+ SwFrame::DestroyFrame(pPage);
+ if ( pStart == pPage )
+ pStart = pNextPage;
+ pPage = pNextPage;
+ if (bUpdatePrev)
+ *ppPrev = pNextPage;
+ continue;
+ }
+ else if ( bIsEmpty && !pFormatWish && //2.
+ pDesc != pPage->GetPageDesc() )
+ {
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 2 - set desc p: " << pPage << " d: " << pDesc );
+ pPage->SetPageDesc( pDesc, nullptr );
+ }
+ else if ( !bIsEmpty && //3.
+ bIsOdd != bWantOdd &&
+ ( ( !pPrevPage && !bWantOdd ) ||
+ ( pPrevPage && !pPrevPage->IsEmptyPage() )
+ )
+ )
+ {
+ if ( pPrevPage )
+ pDesc = pPrevPage->GetPageDesc();
+ SwPageFrame *pTmp = new SwPageFrame( pDoc->GetEmptyPageFormat(), pRoot, pDesc );
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 3 - insert empty p: " << pTmp << " d: " << pDesc );
+ pTmp->Paste( pRoot, pPage );
+ pTmp->PreparePage( false );
+ pPage = pTmp;
+ isPageFrameEmpty = false; // don't delete it right away!
+ }
+ else if ( pPage->GetPageDesc() != pDesc ) //4.
+ {
+ SwPageDesc *pOld = pPage->GetPageDesc();
+ pPage->SetPageDesc( pDesc, pFormatWish );
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 4 - set desc + format p: " << pPage
+ << " d: " << pDesc << " f: " << pFormatWish );
+ if ( bFootnotes )
+ {
+ // If specific values of the FootnoteInfo are changed, something has to happen.
+ // We try to limit the damage...
+ // If the page has no FootnoteCont it might be problematic.
+ // Let's hope that invalidation is enough.
+ SwFootnoteContFrame *pCont = pPage->FindFootnoteCont();
+ if ( pCont && !(pOld->GetFootnoteInfo() == pDesc->GetFootnoteInfo()) )
+ pCont->InvalidateAll_();
+ }
+ }
+ else if ( pFormatWish && pPage->GetFormat() != pFormatWish ) //5.
+ {
+ pPage->SetFrameFormat( pFormatWish );
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 5 - set format p: " << pPage << " f: " << pFormatWish );
+ }
+ else if ( !pFormatWish ) //6.
+ {
+ // get format with inverted logic
+ pFormatWish = bWantOdd ? pDesc->GetLeftFormat() : pDesc->GetRightFormat();
+ if ( pFormatWish && pPage->GetFormat() != pFormatWish )
+ {
+ pPage->SetFrameFormat( pFormatWish );
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 6 - set format p: " << pPage << " f: " << pFormatWish );
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL( "CheckPageDescs, missing solution" );
+ }
+#endif
+ }
+ assert(!bIsEmpty || !isPageFrameEmpty);
+ const bool bWantRemovePage = bIsEmpty || isPageFrameEmpty;
+ if (bWantRemovePage && !pPage->IsDeleteForbidden())
+ {
+ // It also might be that an empty page is not needed at all.
+ // However, the algorithm above cannot determine that. It is not needed if the following
+ // page can live without it. Do obtain that information, we need to dig deeper...
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(pPage->GetNext());
+ if (isPageFrameEmpty || !pPg || pPage->OnRightPage() == pPg->WannaRightPage())
+ {
+ // The following page can find a FrameFormat or has no successor -> empty page not needed
+ SwPageFrame *pTmp = static_cast<SwPageFrame*>(pPage->GetNext());
+ if (isPageFrameEmpty && pPage->GetPrev())
+ { // check previous *again* vs. its new next! see "ooo321_stylepagenumber.odt"
+ pTmp = static_cast<SwPageFrame*>(pPage->GetPrev());
+ }
+ pPage->Cut();
+ bool bUpdatePrev = false;
+ if (ppPrev && *ppPrev == pPage)
+ bUpdatePrev = true;
+ SwFrame::DestroyFrame(pPage);
+ SAL_INFO( "sw.pageframe", "CheckPageDescs - handle bIsEmpty - destroy p: " << pPage );
+ if ( pStart == pPage )
+ pStart = pTmp;
+ pPage = pTmp;
+ if (bUpdatePrev)
+ *ppPrev = pTmp;
+ continue;
+ }
+ }
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+
+ pRoot->SetAssertFlyPages();
+ SwRootFrame::AssertPageFlys( pStart );
+
+ if ( bNotifyFields && (!pImp || !pImp->IsUpdateExpFields()) )
+ {
+ SwDocPosUpdate aMsgHint( nDocPos );
+ pDoc->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ //1. check if two empty pages are behind one another
+ bool bEmpty = false;
+ SwPageFrame *pPg = pStart;
+ while ( pPg )
+ {
+ if ( pPg->IsEmptyPage() )
+ {
+ if ( bEmpty )
+ {
+ OSL_FAIL( "double empty pages." );
+ break; // once is enough
+ }
+ bEmpty = true;
+ }
+ else
+ bEmpty = false;
+
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ }
+#endif
+ SAL_INFO( "sw.pageframe", "CheckPageDescs out)" );
+}
+
+namespace
+{
+ bool isDeleteForbidden(const SwPageFrame *pDel)
+ {
+ if (pDel->IsDeleteForbidden())
+ return true;
+ const SwLayoutFrame* pBody = pDel->FindBodyCont();
+ const SwFrame* pBodyContent = pBody ? pBody->Lower() : nullptr;
+ return pBodyContent && pBodyContent->IsDeleteForbidden();
+ }
+
+ bool doInsertPage( SwRootFrame *pRoot, SwPageFrame **pRefSibling,
+ SwFrameFormat *pFormat, SwPageDesc *pDesc,
+ bool bFootnote, SwPageFrame **pRefPage )
+ {
+ SwPageFrame *pPage = new SwPageFrame(pFormat, pRoot, pDesc);
+ SwPageFrame *pSibling = *pRefSibling;
+ if ( pRefPage )
+ {
+ *pRefPage = pPage;
+ SAL_INFO( "sw.pageframe", "doInsertPage p: " << pPage
+ << " d: " << pDesc << " f: " << pFormat );
+ }
+ else
+ SAL_INFO( "sw.pageframe", "doInsertPage - insert empty p: "
+ << pPage << " d: " << pDesc );
+ pPage->Paste( pRoot, pSibling );
+
+ SwViewShell* pViewShell = pRoot->GetCurrShell();
+ if (pViewShell && pViewShell->GetViewOptions()->IsHideWhitespaceMode())
+ {
+ // Hide-whitespace mode does not shrink the last page, so resize the page that used to
+ // be the last one.
+ if (SwFrame* pPrevPage = pPage->GetPrev())
+ {
+ pPrevPage->InvalidateSize();
+ }
+ }
+
+ pPage->PreparePage( bFootnote );
+ // If the sibling has no body text, destroy it as long as it is no footnote page.
+ if (!pSibling)
+ return true;
+ if (pSibling->IsFootnotePage())
+ return true;
+ if (pSibling->FindFirstBodyContent())
+ return true;
+
+ if (!pRefPage || !isDeleteForbidden(pSibling))
+ {
+ pRoot->RemovePage( pRefSibling, SwRemoveResult::Next ) ;
+ return false;
+ }
+
+ return true;
+ }
+}
+
+SwPageFrame *SwFrame::InsertPage( SwPageFrame *pPrevPage, bool bFootnote )
+{
+ SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPrevPage->GetUpper());
+ SwPageFrame *pSibling = static_cast<SwPageFrame*>(pPrevPage->GetNext());
+ SwPageDesc *pDesc = nullptr;
+
+ // insert right (odd) or left (even) page?
+ bool bNextRightPage = !pPrevPage->OnRightPage();
+ bool bWishedRightPage = bNextRightPage;
+
+ // Which PageDesc is relevant?
+ // For ContentFrame take the one from format if provided,
+ // otherwise from the Follow of the PrevPage
+ if ( IsFlowFrame() && !SwFlowFrame::CastFlowFrame( this )->IsFollow() )
+ {
+ SwFormatPageDesc &rDesc = const_cast<SwFormatPageDesc&>(GetPageDescItem());
+ pDesc = rDesc.GetPageDesc();
+ if ( rDesc.GetNumOffset() )
+ {
+ ::std::optional<sal_uInt16> oNumOffset = rDesc.GetNumOffset();
+ bWishedRightPage = sw::IsRightPageByNumber(*pRoot, *oNumOffset);
+ // use the opportunity to set the flag at root
+ pRoot->SetVirtPageNum( true );
+ }
+ }
+ if ( !pDesc )
+ pDesc = pPrevPage->GetPageDesc()->GetFollow();
+
+ assert(pDesc && "Missing PageDesc");
+ if( !(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()) )
+ bWishedRightPage = !bWishedRightPage;
+ bool const bWishedFirst = pDesc != pPrevPage->GetPageDesc();
+
+ SwDoc *pDoc = pPrevPage->GetFormat()->GetDoc();
+ bool bCheckPages = false;
+ // If there is no FrameFormat for this page, create an empty page.
+ if (bWishedRightPage != bNextRightPage)
+ {
+ if( doInsertPage( pRoot, &pSibling, pDoc->GetEmptyPageFormat(),
+ pPrevPage->GetPageDesc(), bFootnote, nullptr ) )
+ bCheckPages = true;
+ }
+ SwFrameFormat *const pFormat( bWishedRightPage
+ ? pDesc->GetRightFormat(bWishedFirst)
+ : pDesc->GetLeftFormat(bWishedFirst) );
+ assert(pFormat);
+ SwPageFrame *pPage = nullptr;
+ if( doInsertPage( pRoot, &pSibling, pFormat, pDesc, bFootnote, &pPage ) )
+ bCheckPages = true;
+
+ if ( pSibling )
+ {
+ if ( bCheckPages )
+ {
+ CheckPageDescs( pSibling, false );
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
+ if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
+ {
+ const sal_uInt16 nNum = pImp->GetLayAction().GetCheckPageNum();
+ if ( nNum == pPrevPage->GetPhyPageNum() + 1 )
+ {
+ pImp->GetLayAction().SetCheckPageNumDirect(
+ pSibling->GetPhyPageNum() );
+ SAL_INFO( "sw.pageframe", "InsertPage - SetCheckPageNumDirect: "
+ << pSibling->GetPhyPageNum() );
+ }
+ return pPage;
+ }
+ }
+ else
+ SwRootFrame::AssertPageFlys( pSibling );
+ }
+
+ // For the update of page numbering fields, nDocPos provides
+ // the page position from where invalidation should start.
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( !pSh || !pSh->Imp()->IsUpdateExpFields() )
+ {
+ SwDocPosUpdate aMsgHint( pPrevPage->getFrameArea().Top() );
+ pDoc->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ }
+ return pPage;
+}
+
+sw::sidebarwindows::SidebarPosition SwPageFrame::SidebarPosition() const
+{
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( !pSh || pSh->GetViewOptions()->getBrowseMode() )
+ {
+ return sw::sidebarwindows::SidebarPosition::RIGHT;
+ }
+ else
+ {
+ const bool bLTR = getRootFrame()->IsLeftToRightViewLayout();
+ const bool bBookMode = pSh->GetViewOptions()->IsViewLayoutBookMode();
+ const bool bRightSidebar = bLTR ? (!bBookMode || OnRightPage()) : (bBookMode && !OnRightPage());
+
+ return bRightSidebar
+ ? sw::sidebarwindows::SidebarPosition::RIGHT
+ : sw::sidebarwindows::SidebarPosition::LEFT;
+ }
+}
+
+SwTwips SwRootFrame::GrowFrame( SwTwips nDist, bool bTst, bool )
+{
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.AddHeight(nDist );
+ }
+
+ return nDist;
+}
+
+SwTwips SwRootFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool )
+{
+ OSL_ENSURE( nDist >= 0, "nDist < 0." );
+ OSL_ENSURE( nDist <= getFrameArea().Height(), "nDist greater than current size." );
+
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.AddHeight( -nDist );
+ }
+
+ return nDist;
+}
+
+void SwRootFrame::RemovePage( SwPageFrame **pDelRef, SwRemoveResult eResult )
+{
+ SwPageFrame *pDel = *pDelRef;
+ (*pDelRef) = static_cast<SwPageFrame*>(
+ eResult == SwRemoveResult::Next ? pDel->GetNext() : pDel->GetPrev() );
+ if ( !GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
+ RemoveFootnotes( pDel, true );
+ pDel->Cut();
+ SwFrame::DestroyFrame( pDel );
+}
+
+/// remove pages that are not needed at all
+void SwRootFrame::RemoveSuperfluous()
+{
+ // A page is empty if the body text area has no ContentFrame, but not if there
+ // is at least one Fly or one footnote attached to the page. Two runs are
+ // needed: one for endnote pages and one for the pages of the body text.
+
+ if ( !IsSuperfluous() )
+ return;
+ mbCheckSuperfluous = false;
+
+ SwPageFrame *pPage = GetLastPage();
+ tools::Long nDocPos = LONG_MAX;
+
+ // Check the corresponding last page if it is empty and stop loop at the last non-empty page.
+ do
+ {
+ if (!sw::IsPageFrameEmpty(*pPage))
+ {
+ if ( pPage->IsFootnotePage() )
+ {
+ while ( pPage->IsFootnotePage() )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
+ OSL_ENSURE( pPage, "only endnote pages remain." );
+ }
+ continue;
+ }
+ else
+ pPage = nullptr;
+ }
+
+ if ( pPage )
+ {
+ SAL_INFO( "sw.pageframe", "RemoveSuperfluous - DestroyFrm p: " << pPage );
+ RemovePage( &pPage, SwRemoveResult::Prev );
+ nDocPos = pPage ? pPage->getFrameArea().Top() : 0;
+ }
+ } while ( pPage );
+
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( nDocPos != LONG_MAX &&
+ (!pSh || !pSh->Imp()->IsUpdateExpFields()) )
+ {
+ SwDocPosUpdate aMsgHint( nDocPos );
+ GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ }
+}
+
+/// Ensures that enough pages exist, so that all page bound frames and draw objects can be placed
+void SwRootFrame::AssertFlyPages()
+{
+ if ( !IsAssertFlyPages() )
+ return;
+ mbAssertFlyPages = false;
+
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ const SwFrameFormats *pTable = pDoc->GetSpzFrameFormats();
+
+ // what page targets the "last" Fly?
+ // note the needed pages in a set
+ sal_uInt16 nMaxPg(0);
+ o3tl::sorted_vector< sal_uInt16 > neededPages;
+ neededPages.reserve(pTable->size());
+
+ for ( size_t i = 0; i < pTable->size(); ++i )
+ {
+ const SwFormatAnchor &rAnch = (*pTable)[i]->GetAnchor();
+ if(!rAnch.GetContentAnchor())
+ {
+ const sal_uInt16 nPageNum(rAnch.GetPageNum());
+
+ // calc MaxPage (as before)
+ nMaxPg = std::max(nMaxPg, nPageNum);
+
+ // note as needed page
+ neededPages.insert(nPageNum);
+ }
+ }
+
+ // How many pages exist at the moment?
+ // And are there EmptyPages that are needed?
+ SwPageFrame* pPage(static_cast<SwPageFrame*>(Lower()));
+ SwPageFrame* pPrevPage(nullptr);
+ SwPageFrame* pFirstRevivedEmptyPage(nullptr);
+
+ while(pPage) // moved two while-conditions to break-statements (see below)
+ {
+ const sal_uInt16 nPageNum(pPage->GetPhyPageNum());
+
+ if(pPage->IsEmptyPage() &&
+ nullptr != pPrevPage &&
+ neededPages.find(nPageNum) != neededPages.end())
+ {
+ // This is an empty page, but it *is* needed since a SwFrame
+ // is anchored at it directly. Initially these SwFrames are
+ // not fully initialized. Need to change the format of this SwFrame
+ // and let the ::Notify mechanism newly evaluate
+ // m_bEmptyPage (see SwPageFrame::UpdateAttr_). Code is taken and
+ // adapted from ::InsertPage (used below), this needs previous page
+ bool bWishedRightPage(!pPrevPage->OnRightPage());
+ SwPageDesc* pDesc(pPrevPage->GetPageDesc()->GetFollow());
+ assert(pDesc && "Missing PageDesc");
+
+ if (!(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()))
+ {
+ bWishedRightPage = !bWishedRightPage;
+ }
+
+ bool const bWishedFirst(pDesc != pPrevPage->GetPageDesc());
+ SwFrameFormat* pFormat(bWishedRightPage ? pDesc->GetRightFormat(bWishedFirst) : pDesc->GetLeftFormat(bWishedFirst));
+
+ // set SwFrameFormat, this will trigger SwPageFrame::UpdateAttr_ and re-evaluate
+ // m_bEmptyPage, too
+ pPage->SetFrameFormat(pFormat);
+
+ if(nullptr == pFirstRevivedEmptyPage)
+ {
+ // remember first (lowest) SwPageFrame which needed correction
+ pFirstRevivedEmptyPage = pPage;
+ }
+ }
+
+ // original while-condition II
+ if(nullptr == pPage->GetNext())
+ {
+ break;
+ }
+
+ // original while-condition III
+ if(static_cast< SwPageFrame* >(pPage->GetNext())->IsFootnotePage())
+ {
+ break;
+ }
+
+ pPrevPage = pPage;
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+
+ if ( nMaxPg > pPage->GetPhyPageNum() )
+ {
+ for ( sal_uInt16 i = pPage->GetPhyPageNum(); i < nMaxPg; ++i )
+ pPage = InsertPage( pPage, false );
+
+ // If the endnote pages are now corrupt, destroy them.
+ if ( !pDoc->GetFootnoteIdxs().empty() )
+ {
+ pPage = static_cast<SwPageFrame*>(Lower());
+ while ( pPage && !pPage->IsFootnotePage() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+
+ if ( pPage )
+ {
+ SwPageDesc *pTmpDesc = pPage->FindPageDesc();
+ bool isRightPage = pPage->OnRightPage();
+ if ( pPage->GetFormat() !=
+ (isRightPage ? pTmpDesc->GetRightFormat() : pTmpDesc->GetLeftFormat()) )
+ RemoveFootnotes( pPage, false, true );
+ }
+ }
+ }
+
+ // if we corrected SwFrameFormat and changed one (or more) m_bEmptyPage
+ // flags, we need to correct evtl. currently wrong positioned SwFrame(s)
+ // which did think until now that these Page(s) are empty.
+ // After trying to correct myself I found SwRootFrame::AssertPageFlys
+ // directly below that already does that, so use it.
+ if(nullptr != pFirstRevivedEmptyPage)
+ {
+ AssertPageFlys(pFirstRevivedEmptyPage);
+ }
+
+ //Remove masters that haven't been replaced yet from the list.
+ RemoveMasterObjs( mpDrawPage );
+
+#if OSL_DEBUG_LEVEL > 0
+ pPage = static_cast<SwPageFrame*>(Lower());
+ while ( pPage && pPage->GetNext() &&
+ !static_cast<SwPageFrame*>(pPage->GetNext())->IsFootnotePage() )
+ {
+ SAL_INFO( "sw.pageframe", "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
+ << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
+ << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ SAL_INFO( "sw.pageframe", "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
+ << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
+ << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
+#endif
+}
+
+/// Ensure that after the given page all page-bound objects are located on the correct page
+void SwRootFrame::AssertPageFlys( SwPageFrame *pPage )
+{
+ SAL_INFO( "sw.pageframe", "(AssertPageFlys in" );
+ while ( pPage )
+ {
+ if (pPage->GetSortedObjs())
+ {
+ size_t i = 0;
+ while ( pPage->GetSortedObjs() && i< pPage->GetSortedObjs()->size() )
+ {
+ // #i28701#
+ SwFrameFormat& rFormat = (*pPage->GetSortedObjs())[i]->GetFrameFormat();
+ const SwFormatAnchor &rAnch = rFormat.GetAnchor();
+ const sal_uInt16 nPg = rAnch.GetPageNum();
+ if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE) &&
+ nPg != pPage->GetPhyPageNum() )
+ {
+ SAL_INFO( "sw.pageframe", nPg << " " << pPage->GetPhyPageNum() );
+ // If on the wrong page, check if previous page is empty
+ if( nPg && !(pPage->GetPhyPageNum()-1 == nPg &&
+ static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage()) )
+ {
+ // It can move by itself. Just send a modify to its anchor attribute.
+#if OSL_DEBUG_LEVEL > 1
+ const size_t nCnt = pPage->GetSortedObjs()->size();
+ rFormat.CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
+ OSL_ENSURE( !pPage->GetSortedObjs() ||
+ nCnt != pPage->GetSortedObjs()->size(),
+ "Object couldn't be reattached!" );
+#else
+ rFormat.CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
+#endif
+ // Do not increment index, in this case
+ continue;
+ }
+ }
+ ++i;
+ }
+ }
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ SAL_INFO( "sw.pageframe", "AssertPageFlys out)" );
+}
+
+Size SwRootFrame::ChgSize( const Size& aNewSize )
+{
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.SSize(aNewSize);
+ }
+
+ InvalidatePrt_();
+ mbFixSize = false;
+ return getFrameArea().SSize();
+}
+
+void SwRootFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
+{
+ if ( !isFrameAreaPositionValid() )
+ {
+ setFrameAreaPositionValid(true);
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(DOCUMENTBORDER);
+ aFrm.Pos().setY(DOCUMENTBORDER);
+ }
+
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Pos().setX(0);
+ aPrt.Pos().setY(0);
+ aPrt.SSize( getFrameArea().SSize() );
+ }
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ // SSize is set by the pages (Cut/Paste).
+ setFrameAreaSizeValid(true);
+ }
+}
+
+void SwRootFrame::ImplInvalidateBrowseWidth()
+{
+ mbBrowseWidthValid = false;
+ SwFrame *pPg = Lower();
+ while ( pPg )
+ {
+ pPg->InvalidateSize();
+ pPg = pPg->GetNext();
+ }
+}
+
+void SwRootFrame::ImplCalcBrowseWidth()
+{
+ OSL_ENSURE( GetCurrShell() && GetCurrShell()->GetViewOptions()->getBrowseMode(),
+ "CalcBrowseWidth and not in BrowseView" );
+
+ // The (minimal) with is determined from borders, tables and paint objects.
+ // It is calculated based on the attributes. Thus, it is not relevant how wide they are
+ // currently but only how wide they want to be.
+ // Frames and paint objects inside other objects (frames, tables) do not count.
+ // Borders and columns are not taken into account.
+
+ SwFrame *pFrame = ContainsContent();
+ while ( pFrame && !pFrame->IsInDocBody() )
+ pFrame = static_cast<SwContentFrame*>(pFrame)->GetNextContentFrame();
+ if ( !pFrame )
+ return;
+
+ mbBrowseWidthValid = true;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ mnBrowseWidth = (!comphelper::LibreOfficeKit::isActive() && pSh)? MINLAY + 2 * pSh->GetOut()-> PixelToLogic( pSh->GetBrowseBorder() ).Width(): MIN_BROWSE_WIDTH;
+
+ do
+ {
+ if ( pFrame->IsInTab() )
+ pFrame = pFrame->FindTabFrame();
+
+ if ( pFrame->IsTabFrame() &&
+ !static_cast<SwLayoutFrame*>(pFrame)->GetFormat()->GetFrameSize().GetWidthPercent() )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ const SwFormatHoriOrient &rHori = rAttrs.GetAttrSet().GetHoriOrient();
+ tools::Long nWidth = rAttrs.GetSize().Width();
+ if ( nWidth < int(USHRT_MAX)-2000 && //-2k, because USHRT_MAX gets missing while trying to resize! (and cast to int to avoid -Wsign-compare due to broken USHRT_MAX on Android)
+ text::HoriOrientation::FULL != rHori.GetHoriOrient() )
+ {
+ const SwHTMLTableLayout *pLayoutInfo =
+ static_cast<const SwTabFrame *>(pFrame)->GetTable()
+ ->GetHTMLTableLayout();
+ if ( pLayoutInfo )
+ nWidth = std::min( nWidth, pLayoutInfo->GetBrowseWidthMin() );
+
+ switch ( rHori.GetHoriOrient() )
+ {
+ case text::HoriOrientation::NONE:
+ // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
+ nWidth += rAttrs.CalcLeft( pFrame ) + rAttrs.CalcRight( pFrame );
+ break;
+ case text::HoriOrientation::LEFT_AND_WIDTH:
+ nWidth += rAttrs.CalcLeft( pFrame );
+ break;
+ default:
+ break;
+ }
+ mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
+ }
+ }
+ else if ( pFrame->GetDrawObjs() )
+ {
+ for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
+ {
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
+ const SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
+ const bool bFly = pAnchoredObj->DynCastFlyFrame() != nullptr;
+ if ((bFly && (FAR_AWAY == pAnchoredObj->GetObjRect().Width()))
+ || rFormat.GetFrameSize().GetWidthPercent())
+ {
+ continue;
+ }
+
+ tools::Long nWidth = 0;
+ switch ( rFormat.GetAnchor().GetAnchorId() )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ nWidth = bFly ? rFormat.GetFrameSize().GetWidth() :
+ pAnchoredObj->GetObjRect().Width();
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ {
+ // #i33170#
+ // Reactivated old code because
+ // nWidth = pAnchoredObj->GetObjRect().Right()
+ // gives wrong results for objects that are still
+ // at position FAR_AWAY.
+ if ( bFly )
+ {
+ nWidth = rFormat.GetFrameSize().GetWidth();
+ const SwFormatHoriOrient &rHori = rFormat.GetHoriOrient();
+ switch ( rHori.GetHoriOrient() )
+ {
+ case text::HoriOrientation::NONE:
+ nWidth += rHori.GetPos();
+ break;
+ case text::HoriOrientation::INSIDE:
+ case text::HoriOrientation::LEFT:
+ if ( text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() )
+ nWidth += pFrame->getFramePrintArea().Left();
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ // Paint objects to not have attributes and
+ // are defined by their current size
+ nWidth = pAnchoredObj->GetObjRect().Right() -
+ pAnchoredObj->GetDrawObj()->GetAnchorPos().X();
+ }
+ break;
+ default: /* do nothing */;
+ }
+ mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
+ }
+ }
+ pFrame = pFrame->FindNextCnt();
+ } while ( pFrame );
+}
+
+void SwRootFrame::StartAllAction()
+{
+ if ( GetCurrShell() )
+ for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
+ {
+ if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
+ pCursorShell->StartAction();
+ else
+ rSh.StartAction();
+ }
+}
+
+void SwRootFrame::EndAllAction( bool bVirDev )
+{
+ if ( !GetCurrShell() )
+ return;
+
+ for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
+ {
+ const bool bOldEndActionByVirDev = rSh.IsEndActionByVirDev();
+ rSh.SetEndActionByVirDev( bVirDev );
+ if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
+ {
+ pCursorShell->EndAction();
+ pCursorShell->CallChgLnk();
+ if ( auto pFEShell = dynamic_cast<SwFEShell*>( &rSh) )
+ pFEShell->SetChainMarker();
+ }
+ else
+ rSh.EndAction();
+ rSh.SetEndActionByVirDev( bOldEndActionByVirDev );
+ }
+}
+
+void SwRootFrame::UnoRemoveAllActions()
+{
+ if ( !GetCurrShell() )
+ return;
+
+ for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
+ {
+ // #i84729#
+ // No end action, if <SwViewShell> instance is currently in its end action.
+ // Recursive calls to <::EndAction()> are not allowed.
+ if ( !rSh.IsInEndAction() )
+ {
+ OSL_ENSURE(!rSh.GetRestoreActions(), "Restore action count is already set!");
+ bool bCursor = dynamic_cast<const SwCursorShell*>( &rSh) != nullptr;
+ bool bFE = dynamic_cast<const SwFEShell*>( &rSh) != nullptr;
+ sal_uInt16 nRestore = 0;
+ while( rSh.ActionCount() )
+ {
+ if( bCursor )
+ {
+ static_cast<SwCursorShell*>(&rSh)->EndAction();
+ static_cast<SwCursorShell*>(&rSh)->CallChgLnk();
+ if ( bFE )
+ static_cast<SwFEShell*>(&rSh)->SetChainMarker();
+ }
+ else
+ rSh.EndAction();
+ nRestore++;
+ }
+ rSh.SetRestoreActions(nRestore);
+ }
+ rSh.LockView(true);
+ }
+}
+
+void SwRootFrame::UnoRestoreAllActions()
+{
+ if ( !GetCurrShell() )
+ return;
+
+ for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
+ {
+ sal_uInt16 nActions = rSh.GetRestoreActions();
+ while( nActions-- )
+ {
+ if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
+ pCursorShell->StartAction();
+ else
+ rSh.StartAction();
+ }
+ rSh.SetRestoreActions(0);
+ rSh.LockView(false);
+ }
+}
+
+// Helper functions for SwRootFrame::CheckViewLayout
+static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset );
+
+static void lcl_MoveAllLowerObjs( SwFrame* pFrame, const Point& rOffset )
+{
+ const bool bPage = pFrame->IsPageFrame();
+ const SwSortedObjs* pSortedObj = bPage
+ ? static_cast<SwPageFrame*>(pFrame)->GetSortedObjs()
+ : pFrame->GetDrawObjs();
+ if (pSortedObj == nullptr)
+ return;
+
+ // note: pSortedObj elements may be removed and inserted from
+ // MoveObjectIfActive(), invalidating iterators
+ // DO NOT CONVERT THIS TO A C++11 FOR LOOP, IT DID NOT WORK THE LAST 2 TIMES
+ for (size_t i = 0; i < pSortedObj->size(); ++i)
+ {
+ SwAnchoredObject *const pAnchoredObj = (*pSortedObj)[i];
+ const SwFrameFormat& rObjFormat = pAnchoredObj->GetFrameFormat();
+ const SwFormatAnchor& rAnchor = rObjFormat.GetAnchor();
+
+ // all except from the as character anchored objects are moved
+ // when processing the page frame:
+ if ( !bPage && (rAnchor.GetAnchorId() != RndStdIds::FLY_AS_CHAR) )
+ continue;
+
+ SwObjPositioningInProgress aPosInProgress( *pAnchoredObj );
+
+ if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ lcl_MoveAllLowers( pFlyFrame, rOffset );
+ // tdf#138785 update position specific to as-char flys
+ if (pFlyFrame->IsFlyInContentFrame())
+ {
+ static_cast<SwFlyInContentFrame*>(pFlyFrame)->AddRefOfst(rOffset);
+ }
+ pFlyFrame->NotifyDrawObj();
+ // --> let the active embedded object be moved
+ SwFrame* pLower = pFlyFrame->Lower();
+ if ( pLower && pLower->IsNoTextFrame() )
+ {
+ SwRootFrame* pRoot = pLower->getRootFrame();
+ SwViewShell *pSh = pRoot ? pRoot->GetCurrShell() : nullptr;
+ if ( pSh )
+ {
+ SwNoTextFrame *const pContentFrame = static_cast<SwNoTextFrame*>(pLower);
+ SwOLENode* pNode = pContentFrame->GetNode()->GetOLENode();
+ if ( pNode )
+ {
+ svt::EmbeddedObjectRef& xObj = pNode->GetOLEObj().GetObject();
+ if ( xObj.is() )
+ {
+ for(SwViewShell& rSh : pSh->GetRingContainer())
+ {
+ SwFEShell* pFEShell = dynamic_cast< SwFEShell* >( &rSh );
+ if ( pFEShell )
+ pFEShell->MoveObjectIfActive( xObj, rOffset );
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ( auto pAnchoredDrawObj = dynamic_cast<SwAnchoredDrawObject *>( pAnchoredObj ) )
+ {
+ // don't touch objects that are not yet positioned:
+ if ( pAnchoredDrawObj->NotYetPositioned() )
+ continue;
+
+ const Point& aCurrAnchorPos = pAnchoredDrawObj->GetDrawObj()->GetAnchorPos();
+ const Point aNewAnchorPos( aCurrAnchorPos + rOffset );
+ pAnchoredDrawObj->DrawObj()->SetAnchorPos( aNewAnchorPos );
+ pAnchoredDrawObj->SetLastObjRect( pAnchoredDrawObj->GetObjRect().SVRect() );
+
+ // clear contour cache
+ if ( pAnchoredDrawObj->GetFrameFormat().GetSurround().IsContour() )
+ ClrContourCache( pAnchoredDrawObj->GetDrawObj() );
+ }
+ // #i92511#
+ // cache for object rectangle inclusive spaces has to be invalidated.
+ pAnchoredObj->InvalidateObjRectWithSpaces();
+ }
+}
+
+static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset )
+{
+ // first move the current frame
+ // RotateFlyFrame3: moved to transform_translate instead of
+ // direct modification to allow the SwFrame evtl. needed own reactions
+ pFrame->transform_translate(rOffset);
+
+ // Don't forget accessibility:
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( pFrame->IsAccessibleFrame() )
+ {
+ SwRootFrame *pRootFrame = pFrame->getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ const SwRect aFrame( pFrame->getFrameArea() );
+
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pFrame, aFrame );
+ }
+ }
+#endif
+
+ // the move any objects
+ lcl_MoveAllLowerObjs( pFrame, rOffset );
+
+ // finally, for layout frames we have to call this function recursively:
+ if (pFrame->IsLayoutFrame())
+ {
+ SwFrame* pLowerFrame = pFrame->GetLower();
+ while ( pLowerFrame )
+ {
+ lcl_MoveAllLowers( pLowerFrame, rOffset );
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+ }
+}
+
+// Calculate how the pages have to be positioned
+void SwRootFrame::CheckViewLayout( const SwViewOption* pViewOpt, const SwRect* pVisArea )
+{
+ SwViewShell* pSh = GetCurrShell();
+ vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
+ // #i91432#
+ // No calculation of page positions, if only an empty page is present.
+ // This situation occurs when <SwRootFrame> instance is in construction
+ // and the document contains only left pages.
+ if ( Lower()->GetNext() == nullptr &&
+ static_cast<SwPageFrame*>(Lower())->IsEmptyPage() )
+ {
+ return;
+ }
+
+ if ( !pVisArea )
+ {
+ // no early return for bNewPage
+ if ( mnViewWidth < 0 )
+ mnViewWidth = 0;
+ }
+ else
+ {
+ assert(pViewOpt && "CheckViewLayout required ViewOptions");
+
+ const sal_uInt16 nColumns = pViewOpt->GetViewLayoutColumns();
+ const bool bBookMode = pViewOpt->IsViewLayoutBookMode();
+
+ if ( nColumns == mnColumns && bBookMode == mbBookMode && pVisArea->Width() == mnViewWidth && !mbSidebarChanged )
+ return;
+
+ mnColumns = nColumns;
+ mbBookMode = bBookMode;
+ mnViewWidth = pVisArea->Width();
+ mbSidebarChanged = false;
+ }
+
+ if( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE ) )
+ {
+ mnColumns = 1;
+ mbBookMode = false;
+ }
+
+ Calc(pRenderContext);
+
+ const bool bOldCallbackActionEnabled = IsCallbackActionEnabled();
+ SetCallbackActionEnabled( false );
+
+ maPageRects.clear();
+
+ const tools::Long nBorder = getFrameArea().Pos().getX();
+ const tools::Long nVisWidth = mnViewWidth - 2 * nBorder;
+ const tools::Long nGapBetweenPages = pViewOpt ? pViewOpt->GetGapBetweenPages()
+ : (pSh ? pSh->GetViewOptions()->GetGapBetweenPages()
+ : SwViewOption::defGapBetweenPages);
+
+ // check how many pages fit into the first page layout row:
+ SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(Lower());
+
+ // will contain the number of pages per row. 0 means that
+ // the page does not fit.
+ tools::Long nWidthRemain = nVisWidth;
+
+ // after one row has been processed, these variables contain
+ // the width of the row and the maximum of the page heights
+ tools::Long nCurrentRowHeight = 0;
+ tools::Long nCurrentRowWidth = 0;
+
+ // these variables are used to finally set the size of the
+ // root frame
+ tools::Long nSumRowHeight = 0;
+ SwTwips nMinPageLeft = TWIPS_MAX;
+ SwTwips nMaxPageRight = 0;
+ SwPageFrame* pStartOfRow = pPageFrame;
+ sal_uInt16 nNumberOfPagesInRow = mbBookMode ? 1 : 0; // in book view, start with right page
+ bool bFirstRow = true;
+
+ bool bPageChanged = false;
+ const bool bRTL = !IsLeftToRightViewLayout();
+ const SwTwips nSidebarWidth = SwPageFrame::GetSidebarBorderWidth( pSh );
+
+ while ( pPageFrame )
+ {
+ // we consider the current page to be "start of row" if
+ // 1. it is the first page in the current row or
+ // 2. it is the second page in the row and the first page is an empty page in non-book view:
+ const bool bStartOfRow = pPageFrame == pStartOfRow ||
+ ( pStartOfRow->IsEmptyPage() && pPageFrame == pStartOfRow->GetNext() && !mbBookMode );
+
+ const bool bEmptyPage = pPageFrame->IsEmptyPage() && !mbBookMode;
+
+ // no half doc border space for first page in each row and
+ tools::Long nPageWidth = 0;
+ tools::Long nPageHeight = 0;
+
+ if ( mbBookMode )
+ {
+ const SwFrame& rFormatPage = pPageFrame->GetFormatPage();
+
+ nPageWidth = rFormatPage.getFrameArea().Width() + nSidebarWidth + ((bStartOfRow || 1 == (pPageFrame->GetPhyPageNum()%2)) ? 0 : nGapBetweenPages);
+ nPageHeight = rFormatPage.getFrameArea().Height() + nGapBetweenPages;
+ }
+ else
+ {
+ if ( !pPageFrame->IsEmptyPage() )
+ {
+ nPageWidth = pPageFrame->getFrameArea().Width() + nSidebarWidth + (bStartOfRow ? 0 : nGapBetweenPages);
+ nPageHeight = pPageFrame->getFrameArea().Height() + nGapBetweenPages;
+ }
+ }
+
+ if ( !bEmptyPage )
+ ++nNumberOfPagesInRow;
+
+ // finish current row if
+ // 1. in dynamic mode the current page does not fit anymore or
+ // 2. the current page exceeds the maximum number of columns
+ bool bRowFinished = (0 == mnColumns && nWidthRemain < nPageWidth ) ||
+ (0 != mnColumns && mnColumns < nNumberOfPagesInRow);
+
+ // make sure that at least one page goes to the current row:
+ if ( !bRowFinished || bStartOfRow )
+ {
+ // current page is allowed to be in current row
+ nWidthRemain = nWidthRemain - nPageWidth;
+
+ nCurrentRowWidth = nCurrentRowWidth + nPageWidth;
+ nCurrentRowHeight = std::max( nCurrentRowHeight, nPageHeight );
+
+ pPageFrame = static_cast<SwPageFrame*>(pPageFrame->GetNext());
+
+ if ( !pPageFrame )
+ bRowFinished = true;
+ }
+
+ if ( bRowFinished )
+ {
+ // pPageFrame now points to the first page in the new row or null
+ // pStartOfRow points to the first page in the current row
+
+ // special centering for last row. pretend to fill the last row with virtual copies of the last page before centering:
+ if ( !pPageFrame && nWidthRemain > 0 )
+ {
+ // find last page in current row:
+ const SwPageFrame* pLastPageInCurrentRow = pStartOfRow;
+ while( pLastPageInCurrentRow->GetNext() )
+ pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetNext());
+
+ if ( pLastPageInCurrentRow->IsEmptyPage() )
+ pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetPrev());
+
+ // check how many times the last page would still fit into the remaining space:
+ sal_uInt16 nNumberOfVirtualPages = 0;
+ const sal_uInt16 nMaxNumberOfVirtualPages = mnColumns > 0 ? mnColumns - nNumberOfPagesInRow : USHRT_MAX;
+ SwTwips nRemain = nWidthRemain;
+ SwTwips nVirtualPagesWidth = 0;
+ SwTwips nLastPageWidth = pLastPageInCurrentRow->getFrameArea().Width() + nSidebarWidth;
+
+ while ( ( mnColumns > 0 || nRemain > 0 ) && nNumberOfVirtualPages < nMaxNumberOfVirtualPages )
+ {
+ SwTwips nLastPageWidthWithGap = nLastPageWidth;
+ if ( !mbBookMode || ( 0 == (nNumberOfVirtualPages + nNumberOfPagesInRow) %2) )
+ nLastPageWidthWithGap += nGapBetweenPages;
+
+ if ( mnColumns > 0 || nLastPageWidthWithGap < nRemain )
+ {
+ ++nNumberOfVirtualPages;
+ nVirtualPagesWidth += nLastPageWidthWithGap;
+ }
+ nRemain = nRemain - nLastPageWidthWithGap;
+ }
+
+ nCurrentRowWidth = nCurrentRowWidth + nVirtualPagesWidth;
+ }
+
+ // first page in book mode is always special:
+ if ( bFirstRow && mbBookMode )
+ {
+ // #i88036#
+ nCurrentRowWidth +=
+ pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
+ }
+
+ // center page if possible
+ tools::Long nSizeDiff = 0;
+ if (nVisWidth > nCurrentRowWidth && !comphelper::LibreOfficeKit::isActive())
+ nSizeDiff = ( nVisWidth - nCurrentRowWidth ) / 2;
+
+ // adjust positions of pages in current row
+ tools::Long nX = nSizeDiff;
+
+ const tools::Long nRowStart = nBorder + nSizeDiff;
+ const tools::Long nRowEnd = nRowStart + nCurrentRowWidth;
+
+ if ( bFirstRow && mbBookMode )
+ {
+ // #i88036#
+ nX += pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
+ }
+
+ SwPageFrame* pEndOfRow = pPageFrame;
+ SwPageFrame* pPageToAdjust = pStartOfRow;
+
+ do
+ {
+ const SwPageFrame* pFormatPage = pPageToAdjust;
+ if ( mbBookMode )
+ pFormatPage = &pPageToAdjust->GetFormatPage();
+
+ const SwTwips nCurrentPageWidth = pFormatPage->getFrameArea().Width() + (pFormatPage->IsEmptyPage() ? 0 : nSidebarWidth);
+ const Point aOldPagePos = pPageToAdjust->getFrameArea().Pos();
+ const bool bLeftSidebar = pPageToAdjust->SidebarPosition() == sw::sidebarwindows::SidebarPosition::LEFT;
+ const SwTwips nLeftPageAddOffset = bLeftSidebar ?
+ nSidebarWidth :
+ 0;
+
+ Point aNewPagePos( nBorder + nX, nBorder + nSumRowHeight );
+ Point aNewPagePosWithLeftOffset( nBorder + nX + nLeftPageAddOffset, nBorder + nSumRowHeight );
+
+ // RTL view layout: Calculate mirrored page position
+ if ( bRTL )
+ {
+ const tools::Long nXOffsetInRow = aNewPagePos.getX() - nRowStart;
+ aNewPagePos.setX(nRowEnd - nXOffsetInRow - nCurrentPageWidth);
+ aNewPagePosWithLeftOffset = aNewPagePos;
+ aNewPagePosWithLeftOffset.setX(aNewPagePosWithLeftOffset.getX() + nLeftPageAddOffset);
+ }
+
+ if ( aNewPagePosWithLeftOffset != aOldPagePos )
+ {
+ lcl_MoveAllLowers( pPageToAdjust, aNewPagePosWithLeftOffset - aOldPagePos );
+ pPageToAdjust->SetCompletePaint();
+ bPageChanged = true;
+ }
+
+ // calculate area covered by the current page and store to
+ // maPageRects. This is used e.g., for cursor setting
+ const bool bFirstColumn = pPageToAdjust == pStartOfRow;
+ const bool bLastColumn = pPageToAdjust->GetNext() == pEndOfRow;
+ const bool bLastRow = !pEndOfRow;
+
+ nMinPageLeft = std::min( nMinPageLeft, SwTwips(aNewPagePos.getX()) );
+ nMaxPageRight = std::max( nMaxPageRight, SwTwips(aNewPagePos.getX() + nCurrentPageWidth));
+
+ // border of nGapBetweenPages around the current page:
+ SwRect aPageRectWithBorders( aNewPagePos.getX() - nGapBetweenPages,
+ aNewPagePos.getY(),
+ pPageToAdjust->getFrameArea().SSize().Width() + nGapBetweenPages + nSidebarWidth,
+ nCurrentRowHeight );
+
+ static const tools::Long nOuterClickDiff = 1000000;
+
+ // adjust borders for these special cases:
+ if ( (bFirstColumn && !bRTL) || (bLastColumn && bRTL) )
+ aPageRectWithBorders.SubLeft( nOuterClickDiff );
+ if ( (bLastColumn && !bRTL) || (bFirstColumn && bRTL) )
+ aPageRectWithBorders.AddRight( nOuterClickDiff );
+ if ( bFirstRow )
+ aPageRectWithBorders.SubTop( nOuterClickDiff );
+ if ( bLastRow )
+ aPageRectWithBorders.AddBottom( nOuterClickDiff );
+
+ maPageRects.push_back( aPageRectWithBorders );
+
+ nX = nX + nCurrentPageWidth;
+ pPageToAdjust = static_cast<SwPageFrame*>(pPageToAdjust->GetNext());
+
+ // distance to next page
+ if ( pPageToAdjust && pPageToAdjust != pEndOfRow )
+ {
+ // in book view, we add the x gap before left (even) pages:
+ if ( mbBookMode )
+ {
+ if ( 0 == (pPageToAdjust->GetPhyPageNum()%2) )
+ nX = nX + nGapBetweenPages;
+ }
+ else
+ {
+ // in non-book view, don't add x gap before
+ // 1. the last empty page in a row
+ // 2. after an empty page
+ const bool bDontAddGap = ( pPageToAdjust->IsEmptyPage() && pPageToAdjust->GetNext() == pEndOfRow ) ||
+ ( static_cast<SwPageFrame*>(pPageToAdjust->GetPrev())->IsEmptyPage() );
+
+ if ( !bDontAddGap )
+ nX = nX + nGapBetweenPages;
+ }
+ }
+ }
+ while (pPageToAdjust && pPageToAdjust != pEndOfRow);
+
+ // adjust values for root frame size
+ nSumRowHeight = nSumRowHeight + nCurrentRowHeight;
+
+ // start new row:
+ nCurrentRowHeight = 0;
+ nCurrentRowWidth = 0;
+ pStartOfRow = pEndOfRow;
+ nWidthRemain = nVisWidth;
+ nNumberOfPagesInRow = 0;
+ bFirstRow = false;
+ } // end row finished
+ } // end while
+
+ // set size of root frame:
+ const Size aOldSize( getFrameArea().SSize() );
+ const Size aNewSize( nMaxPageRight - nBorder, nSumRowHeight - nGapBetweenPages );
+
+ if ( bPageChanged || aNewSize != aOldSize )
+ {
+ ChgSize( aNewSize );
+ ::AdjustSizeChgNotify( this );
+ Calc(pRenderContext);
+
+ if ( pSh && pSh->GetDoc()->GetDocShell() )
+ {
+ pSh->SetFirstVisPageInvalid();
+ if (bOldCallbackActionEnabled)
+ {
+ pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
+ pSh->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged));
+ }
+ }
+ }
+
+ maPagesArea.Pos( getFrameArea().Pos() );
+ maPagesArea.SSize( aNewSize );
+ if ( TWIPS_MAX != nMinPageLeft )
+ maPagesArea.Left_( nMinPageLeft );
+
+ SetCallbackActionEnabled( bOldCallbackActionEnabled );
+}
+
+bool SwRootFrame::IsLeftToRightViewLayout() const
+{
+ // Layout direction determined by layout direction of the first page.
+ // #i88036#
+ // Only ask a non-empty page frame for its layout direction
+ assert(dynamic_cast<const SwPageFrame *>(Lower()) != nullptr);
+ const SwPageFrame& rPage = static_cast<const SwPageFrame&>(*Lower()).GetFormatPage();
+ return !rPage.IsRightToLeft() && !rPage.IsVertical();
+}
+
+const SwPageFrame& SwPageFrame::GetFormatPage() const
+{
+ const SwPageFrame* pRet = this;
+ if ( IsEmptyPage() )
+ {
+ pRet = static_cast<const SwPageFrame*>( OnRightPage() ? GetNext() : GetPrev() );
+ // #i88035#
+ // Typically a right empty page frame has a next non-empty page frame and
+ // a left empty page frame has a previous non-empty page frame.
+ // But under certain circumstances this assumption is not true -
+ // e.g. during insertion of a left page at the end of the document right
+ // after a left page in an intermediate state a right empty page does not
+ // have a next page frame.
+ if ( pRet == nullptr )
+ {
+ if ( OnRightPage() )
+ {
+ pRet = static_cast<const SwPageFrame*>( GetPrev() );
+ }
+ else
+ {
+ pRet = static_cast<const SwPageFrame*>( GetNext() );
+ }
+ }
+ assert(pRet &&
+ "<SwPageFrame::GetFormatPage()> - inconsistent layout: empty page without previous and next page frame --> crash.");
+ }
+ return *pRet;
+}
+
+bool SwPageFrame::IsOverHeaderFooterArea( const Point& rPt, FrameControlType &rControl ) const
+{
+ tools::Long nUpperLimit = 0;
+ tools::Long nLowerLimit = 0;
+ const SwFrame* pFrame = Lower();
+ while ( pFrame )
+ {
+ if ( pFrame->IsBodyFrame() )
+ {
+ nUpperLimit = pFrame->getFrameArea().Top();
+ nLowerLimit = pFrame->getFrameArea().Bottom();
+ }
+ else if ( pFrame->IsFootnoteContFrame() )
+ nLowerLimit = pFrame->getFrameArea().Bottom();
+
+ pFrame = pFrame->GetNext();
+ }
+
+ SwRect aHeaderArea( getFrameArea().TopLeft(),
+ Size( getFrameArea().Width(), nUpperLimit - getFrameArea().Top() ) );
+
+ SwViewShell* pViewShell = getRootFrame()->GetCurrShell();
+ const bool bHideWhitespaceMode = pViewShell->GetViewOptions()->IsHideWhitespaceMode();
+ if ( aHeaderArea.Contains( rPt ) )
+ {
+ if (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetHeader().IsActive())
+ {
+ rControl = FrameControlType::Header;
+ return true;
+ }
+ }
+ else
+ {
+ SwRect aFooterArea( Point( getFrameArea().Left(), nLowerLimit ),
+ Size( getFrameArea().Width(), getFrameArea().Bottom() - nLowerLimit ) );
+
+ if ( aFooterArea.Contains( rPt ) &&
+ (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetFooter().IsActive()) )
+ {
+ rControl = FrameControlType::Footer;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SwPageFrame::CheckPageHeightValidForHideWhitespace(SwTwips nDiff)
+{
+ SwViewShell* pShell = getRootFrame()->GetCurrShell();
+ if (pShell && pShell->GetViewOptions()->IsWhitespaceHidden())
+ {
+ // When whitespace is hidden, the page frame has two heights: the
+ // nominal (defined by the frame format), and the actual (which is
+ // at most the nominal height, but can be smaller in case there is
+ // no content for the whole page).
+ // The layout size is the actual one, but we want to move the
+ // content frame to a new page only in case it doesn't fit the
+ // nominal size.
+ if (nDiff < 0)
+ {
+ // Content frame doesn't fit the actual size, check if it fits the nominal one.
+ const SwFrameFormat* pPageFormat = static_cast<const SwFrameFormat*>(GetDep());
+ const Size& rPageSize = pPageFormat->GetFrameSize().GetSize();
+ tools::Long nWhitespace = rPageSize.getHeight() - getFrameArea().Height();
+ if (nWhitespace > -nDiff)
+ {
+ // It does: don't move it and invalidate our page frame so
+ // that it gets a larger height.
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+const SwHeaderFrame* SwPageFrame::GetHeaderFrame() const
+{
+ const SwFrame* pLowerFrame = Lower();
+ while (pLowerFrame)
+ {
+ if (pLowerFrame->IsHeaderFrame())
+ return dynamic_cast<const SwHeaderFrame*>(pLowerFrame);
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+ return nullptr;
+}
+
+const SwFooterFrame* SwPageFrame::GetFooterFrame() const
+{
+ const SwFrame* pLowerFrame = Lower();
+ while (pLowerFrame)
+ {
+ if (pLowerFrame->IsFooterFrame())
+ return dynamic_cast<const SwFooterFrame*>(pLowerFrame);
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+ return nullptr;
+}
+
+SwTextGridItem const* GetGridItem(SwPageFrame const*const pPage)
+{
+ if (pPage && pPage->HasGrid())
+ {
+ SwTextGridItem const& rGridItem(
+ pPage->GetPageDesc()->GetMaster().GetTextGrid());
+ if (GRID_NONE != rGridItem.GetGridType())
+ {
+ return &rGridItem;
+ }
+ }
+ return nullptr;
+}
+
+sal_uInt16 GetGridWidth(SwTextGridItem const& rG, SwDoc const& rDoc)
+{
+ return (rDoc.IsSquaredPageMode()) ? rG.GetBaseHeight() : rG.GetBaseWidth();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/pagedesc.cxx b/sw/source/core/layout/pagedesc.cxx
new file mode 100644
index 000000000..d93b47517
--- /dev/null
+++ b/sw/source/core/layout/pagedesc.cxx
@@ -0,0 +1,803 @@
+/* -*- 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 <libxml/xmlwriter.h>
+
+#include <editeng/pbinitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <fmtclds.hxx>
+#include <fmtfsize.hxx>
+#include <pagefrm.hxx>
+#include <pagedesc.hxx>
+#include <swtable.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <doc.hxx>
+#include <node.hxx>
+#include <strings.hrc>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <poolfmt.hxx>
+#include <calbck.hxx>
+
+SwPageDesc::SwPageDesc(const OUString& rName, SwFrameFormat *pFormat, SwDoc *const pDoc)
+ : sw::BroadcastingModify()
+ , m_StyleName( rName )
+ , m_Master( pDoc->GetAttrPool(), rName, pFormat )
+ , m_Left( pDoc->GetAttrPool(), rName, pFormat )
+ , m_FirstMaster( pDoc->GetAttrPool(), rName, pFormat )
+ , m_FirstLeft( pDoc->GetAttrPool(), rName, pFormat )
+ , m_aStashedHeader()
+ , m_aStashedFooter()
+ , m_aDepends(*this)
+ , m_pTextFormatColl(nullptr)
+ , m_pFollow( this )
+ , m_nRegHeight( 0 )
+ , m_nRegAscent( 0 )
+ , m_nVerticalAdjustment( drawing::TextVerticalAdjust_TOP )
+ , m_eUse( UseOnPage::All | UseOnPage::HeaderShare | UseOnPage::FooterShare | UseOnPage::FirstShare )
+ , m_IsLandscape( false )
+ , m_IsHidden( false )
+ , m_pdList( nullptr )
+{
+}
+
+SwPageDesc::SwPageDesc( const SwPageDesc &rCpy )
+ : sw::BroadcastingModify()
+ , m_StyleName( rCpy.GetName() )
+ , m_NumType( rCpy.GetNumType() )
+ , m_Master( rCpy.GetMaster() )
+ , m_Left( rCpy.GetLeft() )
+ , m_FirstMaster( rCpy.GetFirstMaster() )
+ , m_FirstLeft( rCpy.GetFirstLeft() )
+ , m_aDepends(*this)
+ , m_pTextFormatColl(nullptr)
+ , m_pFollow( rCpy.m_pFollow )
+ , m_nRegHeight( rCpy.GetRegHeight() )
+ , m_nRegAscent( rCpy.GetRegAscent() )
+ , m_nVerticalAdjustment( rCpy.GetVerticalAdjustment() )
+ , m_eUse( rCpy.ReadUseOn() )
+ , m_IsLandscape( rCpy.GetLandscape() )
+ , m_IsHidden( rCpy.IsHidden() )
+ , m_FootnoteInfo( rCpy.GetFootnoteInfo() )
+ , m_pdList( nullptr )
+{
+ m_aStashedHeader.m_pStashedFirst = rCpy.m_aStashedHeader.m_pStashedFirst;
+ m_aStashedHeader.m_pStashedLeft = rCpy.m_aStashedHeader.m_pStashedLeft;
+ m_aStashedHeader.m_pStashedFirstLeft = rCpy.m_aStashedHeader.m_pStashedFirstLeft;
+
+ m_aStashedFooter.m_pStashedFirst = rCpy.m_aStashedFooter.m_pStashedFirst;
+ m_aStashedFooter.m_pStashedLeft = rCpy.m_aStashedFooter.m_pStashedLeft;
+ m_aStashedFooter.m_pStashedFirstLeft = rCpy.m_aStashedFooter.m_pStashedFirstLeft;
+
+ if (rCpy.m_pTextFormatColl && rCpy.m_aDepends.IsListeningTo(rCpy.m_pTextFormatColl))
+ {
+ m_pTextFormatColl = rCpy.m_pTextFormatColl;
+ m_aDepends.StartListening(const_cast<SwTextFormatColl*>(m_pTextFormatColl));
+ }
+}
+
+SwPageDesc & SwPageDesc::operator = (const SwPageDesc & rSrc)
+{
+ if(this == &rSrc)
+ return *this;
+
+ m_StyleName = rSrc.m_StyleName;
+ m_NumType = rSrc.m_NumType;
+ m_Master = rSrc.m_Master;
+ m_Left = rSrc.m_Left;
+ m_FirstMaster = rSrc.m_FirstMaster;
+ m_FirstLeft = rSrc.m_FirstLeft;
+
+ m_aStashedHeader.m_pStashedFirst = rSrc.m_aStashedHeader.m_pStashedFirst;
+ m_aStashedHeader.m_pStashedLeft = rSrc.m_aStashedHeader.m_pStashedLeft;
+ m_aStashedHeader.m_pStashedFirstLeft = rSrc.m_aStashedHeader.m_pStashedFirstLeft;
+
+ m_aStashedFooter.m_pStashedFirst = rSrc.m_aStashedFooter.m_pStashedFirst;
+ m_aStashedFooter.m_pStashedLeft = rSrc.m_aStashedFooter.m_pStashedLeft;
+ m_aStashedFooter.m_pStashedFirstLeft = rSrc.m_aStashedFooter.m_pStashedFirstLeft;
+
+ m_aDepends.EndListeningAll();
+ if (rSrc.m_pTextFormatColl && rSrc.m_aDepends.IsListeningTo(rSrc.m_pTextFormatColl))
+ {
+ m_pTextFormatColl = rSrc.m_pTextFormatColl;
+ m_aDepends.StartListening(const_cast<SwTextFormatColl*>(m_pTextFormatColl));
+ }
+ else
+ m_pTextFormatColl = nullptr;
+
+ if (rSrc.m_pFollow == &rSrc)
+ m_pFollow = this;
+ else
+ m_pFollow = rSrc.m_pFollow;
+
+ m_nRegHeight = rSrc.m_nRegHeight;
+ m_nRegAscent = rSrc.m_nRegAscent;
+ m_nVerticalAdjustment = rSrc.m_nVerticalAdjustment;
+ m_eUse = rSrc.m_eUse;
+ m_IsLandscape = rSrc.m_IsLandscape;
+ return *this;
+}
+
+SwPageDesc::~SwPageDesc()
+{
+}
+
+bool SwPageDesc::SetName( const OUString& rNewName )
+{
+ bool renamed = true;
+ if (m_pdList) {
+ SwPageDescs::iterator it = m_pdList->find_( m_StyleName );
+ if( m_pdList->end() == it ) {
+ SAL_WARN( "sw", "SwPageDesc not found in expected m_pdList" );
+ return false;
+ }
+ renamed = m_pdList->m_PosIndex.modify( it,
+ change_name( rNewName ), change_name( m_StyleName ) );
+ }
+ else
+ m_StyleName = rNewName;
+ return renamed;
+}
+
+/// Only the margin is mirrored.
+/// Attributes like borders and so on are copied 1:1.
+void SwPageDesc::Mirror()
+{
+ //Only the margins are mirrored, all other values are just copied.
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ const SvxLRSpaceItem &rLR = m_Master.GetLRSpace();
+ aLR.SetLeft( rLR.GetRight() );
+ aLR.SetRight( rLR.GetLeft() );
+ aLR.SetRightGutterMargin(rLR.GetGutterMargin());
+
+ SfxItemSet aSet( *m_Master.GetAttrSet().GetPool(),
+ m_Master.GetAttrSet().GetRanges() );
+ aSet.Put( aLR );
+ aSet.Put( m_Master.GetFrameSize() );
+ aSet.Put( m_Master.GetPaperBin() );
+ aSet.Put( m_Master.GetULSpace() );
+ aSet.Put( m_Master.GetBox() );
+ aSet.Put( m_Master.makeBackgroundBrushItem() );
+ aSet.Put( m_Master.GetShadow() );
+ aSet.Put( m_Master.GetCol() );
+ aSet.Put( m_Master.GetFrameDir() );
+ m_Left.SetFormatAttr( aSet );
+}
+
+void SwPageDesc::ResetAllAttr()
+{
+ SwFrameFormat& rFormat = GetMaster();
+
+ // #i73790# - method renamed
+ rFormat.ResetAllFormatAttr();
+ rFormat.SetFormatAttr( SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR) );
+}
+
+// gets information from Modify
+bool SwPageDesc::GetInfo( SfxPoolItem & rInfo ) const
+{
+ if (!m_Master.GetInfo(rInfo))
+ return false; // found
+ if (!m_Left.GetInfo(rInfo))
+ return false ;
+ if ( !m_FirstMaster.GetInfo( rInfo ) )
+ return false;
+ return m_FirstLeft.GetInfo( rInfo );
+}
+
+/// set the style for the grid alignment
+void SwPageDesc::SetRegisterFormatColl(const SwTextFormatColl* pFormat)
+{
+ if(pFormat != m_pTextFormatColl)
+ {
+ m_aDepends.EndListeningAll();
+ m_pTextFormatColl = pFormat;
+ m_aDepends.StartListening(const_cast<SwTextFormatColl*>(m_pTextFormatColl));
+ RegisterChange();
+ }
+}
+
+/// retrieve the style for the grid alignment
+const SwTextFormatColl* SwPageDesc::GetRegisterFormatColl() const
+{
+ if (!m_aDepends.IsListeningTo(m_pTextFormatColl))
+ m_pTextFormatColl = nullptr;
+ return m_pTextFormatColl;
+}
+
+/// notify all affected page frames
+void SwPageDesc::RegisterChange()
+{
+ // #117072# - During destruction of the document <SwDoc>
+ // the page description is modified. Thus, do nothing, if the document
+ // is in destruction respectively if no viewshell exists.
+ SwDoc* pDoc = GetMaster().GetDoc();
+ if ( !pDoc || pDoc->IsInDtor() )
+ {
+ return;
+ }
+ SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( !pSh )
+ {
+ return;
+ }
+
+ m_nRegHeight = 0;
+ {
+ SwIterator<SwFrame,SwFormat> aIter( GetMaster() );
+ for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->IsPageFrame() )
+ static_cast<SwPageFrame*>(pLast)->PrepareRegisterChg();
+ }
+ }
+ {
+ SwIterator<SwFrame,SwFormat> aIter( GetLeft() );
+ for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->IsPageFrame() )
+ static_cast<SwPageFrame*>(pLast)->PrepareRegisterChg();
+ }
+ }
+ {
+ SwIterator<SwFrame,SwFormat> aIter( GetFirstMaster() );
+ for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->IsPageFrame() )
+ static_cast<SwPageFrame*>(pLast)->PrepareRegisterChg();
+ }
+ }
+ {
+ SwIterator<SwFrame,SwFormat> aIter( GetFirstLeft() );
+ for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->IsPageFrame() )
+ static_cast<SwPageFrame*>(pLast)->PrepareRegisterChg();
+ }
+ }
+}
+
+/// special handling if the style of the grid alignment changes
+void SwPageDesc::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const sal_uInt16 nWhich = pLegacyHint->m_pOld
+ ? pLegacyHint->m_pOld->Which()
+ : pLegacyHint->m_pNew
+ ? pLegacyHint->m_pNew->Which()
+ : 0;
+ CallSwClientNotify(rHint);
+ if((RES_ATTRSET_CHG == nWhich)
+ || (RES_FMT_CHG == nWhich)
+ || isCHRATR(nWhich)
+ || (RES_PARATR_LINESPACING == nWhich))
+ RegisterChange();
+ }
+ else if (auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint))
+ {
+ if(m_pTextFormatColl == &rModify)
+ m_pTextFormatColl = static_cast<const SwTextFormatColl*>(pModifyChangedHint->m_pNew);
+ else
+ assert(false);
+ }
+}
+
+static const SwFrame* lcl_GetFrameOfNode( const SwNode& rNd )
+{
+ const sw::BroadcastingModify* pMod;
+ SwFrameType nFrameType = FRM_CNTNT;
+
+ if( rNd.IsContentNode() )
+ {
+ pMod = &static_cast<const SwContentNode&>(rNd);
+ }
+ else if( rNd.IsTableNode() )
+ {
+ pMod = static_cast<const SwTableNode&>(rNd).GetTable().GetFrameFormat();
+ nFrameType = SwFrameType::Tab;
+ }
+ else
+ pMod = nullptr;
+
+ Point aNullPt;
+ std::pair<Point, bool> const tmp(aNullPt, false);
+ return pMod ? ::GetFrameOfModify(nullptr, *pMod, nFrameType, nullptr, &tmp)
+ : nullptr;
+}
+
+const SwPageDesc* SwPageDesc::GetPageDescOfNode(const SwNode& rNd)
+{
+ const SwPageDesc* pRet = nullptr;
+ const SwFrame* pChkFrame = lcl_GetFrameOfNode( rNd );
+ if (pChkFrame && nullptr != (pChkFrame = pChkFrame->FindPageFrame()))
+ pRet = static_cast<const SwPageFrame*>(pChkFrame)->GetPageDesc();
+ return pRet;
+}
+
+const SwFrameFormat* SwPageDesc::GetPageFormatOfNode( const SwNode& rNd,
+ bool bCheckForThisPgDc ) const
+{
+ // which PageDescFormat is valid for this node?
+ const SwFrameFormat* pRet;
+ const SwFrame* pChkFrame = lcl_GetFrameOfNode( rNd );
+
+ if( pChkFrame && nullptr != ( pChkFrame = pChkFrame->FindPageFrame() ))
+ {
+ const SwPageDesc* pPd = bCheckForThisPgDc ? this :
+ static_cast<const SwPageFrame*>(pChkFrame)->GetPageDesc();
+ pRet = &pPd->GetMaster();
+ OSL_ENSURE( static_cast<const SwPageFrame*>(pChkFrame)->GetPageDesc() == pPd, "Wrong node for detection of page format!" );
+ // this page is assigned to which format?
+ if( !pChkFrame->KnowsFormat(*pRet) )
+ {
+ pRet = &pPd->GetLeft();
+ OSL_ENSURE( pChkFrame->KnowsFormat(*pRet), "Wrong node for detection of page format!" );
+ }
+ }
+ else
+ pRet = &GetMaster();
+ return pRet;
+}
+
+bool SwPageDesc::IsFollowNextPageOfNode( const SwNode& rNd ) const
+{
+ bool bRet = false;
+ if( GetFollow() && this != GetFollow() )
+ {
+ const SwFrame* pChkFrame = lcl_GetFrameOfNode( rNd );
+ if( pChkFrame && nullptr != ( pChkFrame = pChkFrame->FindPageFrame() ) &&
+ pChkFrame->IsPageFrame() &&
+ ( !pChkFrame->GetNext() || GetFollow() ==
+ static_cast<const SwPageFrame*>(pChkFrame->GetNext())->GetPageDesc() ))
+ // the page on which the follow points was found
+ bRet = true;
+ }
+ return bRet;
+}
+
+SwFrameFormat *SwPageDesc::GetLeftFormat(bool const bFirst)
+{
+ return (UseOnPage::Left & m_eUse)
+ ? (bFirst ? &m_FirstLeft : &m_Left)
+ : nullptr;
+}
+
+SwFrameFormat *SwPageDesc::GetRightFormat(bool const bFirst)
+{
+ return (UseOnPage::Right & m_eUse)
+ ? (bFirst ? &m_FirstMaster : &m_Master)
+ : nullptr;
+}
+
+bool SwPageDesc::IsFirstShared() const
+{
+ return bool(m_eUse & UseOnPage::FirstShare);
+}
+
+void SwPageDesc::ChgFirstShare( bool bNew )
+{
+ if ( bNew )
+ m_eUse |= UseOnPage::FirstShare;
+ else
+ m_eUse &= UseOnPage::NoFirstShare;
+}
+
+void SwPageDesc::StashFrameFormat(const SwFrameFormat& rFormat, bool bHeader, bool bLeft, bool bFirst)
+{
+ assert(rFormat.GetRegisteredIn());
+ std::shared_ptr<SwFrameFormat>* pFormat = nullptr;
+
+ if (bHeader)
+ {
+ if (bLeft && !bFirst)
+ pFormat = &m_aStashedHeader.m_pStashedLeft;
+ else if (!bLeft && bFirst)
+ pFormat = &m_aStashedHeader.m_pStashedFirst;
+ else if (bLeft && bFirst)
+ pFormat = &m_aStashedHeader.m_pStashedFirstLeft;
+ }
+ else
+ {
+ if (bLeft && !bFirst)
+ pFormat = &m_aStashedFooter.m_pStashedLeft;
+ else if (!bLeft && bFirst)
+ pFormat = &m_aStashedFooter.m_pStashedFirst;
+ else if (bLeft && bFirst)
+ pFormat = &m_aStashedFooter.m_pStashedFirstLeft;
+ }
+
+ if (pFormat)
+ {
+ *pFormat = std::make_shared<SwFrameFormat>(rFormat);
+ }
+ else
+ {
+ SAL_WARN(
+ "sw",
+ "SwPageDesc::StashFrameFormat: Stashing the right page header/footer is pointless.");
+ }
+}
+
+const SwFrameFormat* SwPageDesc::GetStashedFrameFormat(bool bHeader, bool bLeft, bool bFirst) const
+{
+ std::shared_ptr<SwFrameFormat>* pFormat = nullptr;
+
+ if (bLeft && !bFirst)
+ {
+ pFormat = bHeader ? &m_aStashedHeader.m_pStashedLeft : &m_aStashedFooter.m_pStashedLeft;
+ }
+ else if (!bLeft && bFirst)
+ {
+ pFormat = bHeader ? &m_aStashedHeader.m_pStashedFirst : &m_aStashedFooter.m_pStashedFirst;
+ }
+ else if (bLeft && bFirst)
+ {
+ pFormat = bHeader ? &m_aStashedHeader.m_pStashedFirstLeft : &m_aStashedFooter.m_pStashedFirstLeft;
+ }
+
+ if (pFormat)
+ {
+ return pFormat->get();
+ }
+ else
+ {
+ SAL_WARN("sw", "SwPageDesc::GetStashedFrameFormat: Right page format is never stashed.");
+ return nullptr;
+ }
+}
+
+bool SwPageDesc::HasStashedFormat(bool bHeader, bool bLeft, bool bFirst)
+{
+ if (bHeader)
+ {
+ if (bLeft && !bFirst)
+ {
+ return m_aStashedHeader.m_pStashedLeft != nullptr;
+ }
+ else if (!bLeft && bFirst)
+ {
+ return m_aStashedHeader.m_pStashedFirst != nullptr;
+ }
+ else if (bLeft && bFirst)
+ {
+ return m_aStashedHeader.m_pStashedFirstLeft != nullptr;
+ }
+ else
+ {
+ SAL_WARN("sw", "SwPageDesc::HasStashedFormat: Right page format is never stashed.");
+ return false;
+ }
+ }
+ else
+ {
+ if (bLeft && !bFirst)
+ {
+ return m_aStashedFooter.m_pStashedLeft != nullptr;
+ }
+ else if (!bLeft && bFirst)
+ {
+ return m_aStashedFooter.m_pStashedFirst != nullptr;
+ }
+ else if (bLeft && bFirst)
+ {
+ return m_aStashedFooter.m_pStashedFirstLeft != nullptr;
+ }
+ else
+ {
+ SAL_WARN("sw", "SwPageDesc::HasStashedFormat: Right page format is never stashed.");
+ return false;
+ }
+ }
+}
+
+void SwPageDesc::RemoveStashedFormat(bool bHeader, bool bLeft, bool bFirst)
+{
+ if (bHeader)
+ {
+ if (bLeft && !bFirst)
+ {
+ m_aStashedHeader.m_pStashedLeft.reset();
+ }
+ else if (!bLeft && bFirst)
+ {
+ m_aStashedHeader.m_pStashedFirst.reset();
+ }
+ else if (bLeft && bFirst)
+ {
+ m_aStashedHeader.m_pStashedFirstLeft.reset();
+ }
+ else
+ {
+ SAL_WARN("sw", "SwPageDesc::RemoveStashedFormat: Right page format is never stashed.");
+ }
+ }
+ else
+ {
+ if (bLeft && !bFirst)
+ {
+ m_aStashedFooter.m_pStashedLeft.reset();
+ }
+ else if (!bLeft && bFirst)
+ {
+ m_aStashedFooter.m_pStashedFirst.reset();
+ }
+ else if (bLeft && bFirst)
+ {
+ m_aStashedFooter.m_pStashedFirstLeft.reset();
+ }
+ else
+ {
+ SAL_WARN("sw", "SwPageDesc::RemoveStashedFormat: Right page format is never stashed.");
+ }
+ }
+}
+
+// Page styles
+const TranslateId STR_POOLPAGE[] =
+{
+ STR_POOLPAGE_STANDARD,
+ STR_POOLPAGE_FIRST,
+ STR_POOLPAGE_LEFT,
+ STR_POOLPAGE_RIGHT,
+ STR_POOLPAGE_ENVELOPE,
+ STR_POOLPAGE_REGISTER,
+ STR_POOLPAGE_HTML,
+ STR_POOLPAGE_FOOTNOTE,
+ STR_POOLPAGE_ENDNOTE,
+ STR_POOLPAGE_LANDSCAPE
+};
+
+SwPageDesc* SwPageDesc::GetByName(SwDoc& rDoc, std::u16string_view rName)
+{
+ const size_t nDCount = rDoc.GetPageDescCnt();
+
+ for( size_t i = 0; i < nDCount; i++ )
+ {
+ SwPageDesc* pDsc = &rDoc.GetPageDesc( i );
+ if(pDsc->GetName() == rName)
+ {
+ return pDsc;
+ }
+ }
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(STR_POOLPAGE); ++i)
+ {
+ if (rName == SwResId(STR_POOLPAGE[i]))
+ {
+ return rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool( static_cast< sal_uInt16 >(
+ i + RES_POOLPAGE_BEGIN) );
+ }
+ }
+
+ return nullptr;
+}
+
+void SwPageDesc::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPageDesc"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_StyleName"), "%s",
+ BAD_CAST(m_StyleName.toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pFollow"), "%p", m_pFollow);
+ (void)xmlTextWriterWriteFormatAttribute(
+ pWriter, BAD_CAST("m_eUse"), "0x%s",
+ BAD_CAST(OString::number(static_cast<int>(m_eUse), 16).getStr()));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_Master"));
+ m_Master.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_Left"));
+ m_Left.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_FirstMaster"));
+ m_FirstMaster.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_FirstLeft"));
+ m_FirstLeft.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwPageFootnoteInfo::SwPageFootnoteInfo()
+ : m_nMaxHeight( 0 )
+ , m_nLineWidth(10)
+ , m_eLineStyle( SvxBorderLineStyle::SOLID )
+ , m_Width( 25, 100 )
+ , m_nTopDist( 57 ) //1mm
+ , m_nBottomDist( 57 )
+{
+ m_eAdjust = SvxFrameDirection::Horizontal_RL_TB == GetDefaultFrameDirection(GetAppLanguage()) ?
+ css::text::HorizontalAdjust_RIGHT :
+ css::text::HorizontalAdjust_LEFT;
+}
+
+SwPageFootnoteInfo::SwPageFootnoteInfo( const SwPageFootnoteInfo &rCpy )
+ : m_nMaxHeight(rCpy.GetHeight())
+ , m_nLineWidth(rCpy.m_nLineWidth)
+ , m_eLineStyle(rCpy.m_eLineStyle)
+ , m_LineColor(rCpy.m_LineColor)
+ , m_Width(rCpy.GetWidth())
+ , m_eAdjust(rCpy.GetAdj())
+ , m_nTopDist(rCpy.GetTopDist())
+ , m_nBottomDist(rCpy.GetBottomDist())
+{
+}
+
+SwPageFootnoteInfo &SwPageFootnoteInfo::operator=( const SwPageFootnoteInfo& rCpy )
+{
+ m_nMaxHeight = rCpy.GetHeight();
+ m_nLineWidth = rCpy.m_nLineWidth;
+ m_eLineStyle = rCpy.m_eLineStyle;
+ m_LineColor = rCpy.m_LineColor;
+ m_Width = rCpy.GetWidth();
+ m_eAdjust = rCpy.GetAdj();
+ m_nTopDist = rCpy.GetTopDist();
+ m_nBottomDist = rCpy.GetBottomDist();
+ return *this;
+}
+
+bool SwPageFootnoteInfo::operator==( const SwPageFootnoteInfo& rCmp ) const
+{
+ return m_nMaxHeight == rCmp.GetHeight()
+ && m_nLineWidth == rCmp.m_nLineWidth
+ && m_eLineStyle == rCmp.m_eLineStyle
+ && m_LineColor == rCmp.m_LineColor
+ && m_Width == rCmp.GetWidth()
+ && m_eAdjust == rCmp.GetAdj()
+ && m_nTopDist == rCmp.GetTopDist()
+ && m_nBottomDist== rCmp.GetBottomDist();
+}
+
+SwPageDescExt::SwPageDescExt(const SwPageDesc & rPageDesc, SwDoc *const pDoc)
+ : m_PageDesc(rPageDesc)
+ , m_pDoc(pDoc)
+{
+ SetPageDesc(rPageDesc);
+}
+
+SwPageDescExt::SwPageDescExt(const SwPageDescExt & rSrc)
+ : m_PageDesc(rSrc.m_PageDesc)
+ , m_pDoc(rSrc.m_pDoc)
+{
+ SetPageDesc(rSrc.m_PageDesc);
+}
+
+SwPageDescExt::~SwPageDescExt()
+{
+}
+
+OUString const & SwPageDescExt::GetName() const
+{
+ return m_PageDesc.GetName();
+}
+
+void SwPageDescExt::SetPageDesc(const SwPageDesc & rPageDesc)
+{
+ m_PageDesc = rPageDesc;
+
+ if (m_PageDesc.GetFollow())
+ m_sFollow = m_PageDesc.GetFollow()->GetName();
+}
+
+SwPageDescExt & SwPageDescExt::operator = (const SwPageDesc & rSrc)
+{
+ SetPageDesc(rSrc);
+
+ return *this;
+}
+
+SwPageDescExt & SwPageDescExt::operator = (const SwPageDescExt & rSrc)
+{
+ operator=(rSrc.m_PageDesc);
+ return *this;
+}
+
+SwPageDescExt::operator SwPageDesc() const
+{
+ SwPageDesc aResult(m_PageDesc);
+
+ SwPageDesc * pPageDesc = m_pDoc->FindPageDesc(m_sFollow);
+
+ if ( nullptr != pPageDesc )
+ aResult.SetFollow(pPageDesc);
+
+ return aResult;
+}
+
+SwPageDescs::SwPageDescs()
+ : m_PosIndex( m_Array.get<0>() )
+ , m_NameIndex( m_Array.get<1>() )
+{
+}
+
+SwPageDescs::~SwPageDescs()
+{
+ for(const_iterator it = begin(); it != end(); ++it)
+ delete *it;
+}
+
+SwPageDescs::iterator SwPageDescs::find_(const OUString &name) const
+{
+ ByName::iterator it = m_NameIndex.find( name );
+ return m_Array.iterator_to( *it );
+}
+
+std::pair<SwPageDescs::const_iterator,bool> SwPageDescs::push_back( const value_type& x )
+{
+ // SwPageDesc is not already in a SwPageDescs list!
+ assert( x->m_pdList == nullptr );
+
+ std::pair<iterator,bool> res = m_PosIndex.push_back( x );
+ if( res.second )
+ x->m_pdList = this;
+ return res;
+}
+
+void SwPageDescs::erase( const value_type& x )
+{
+ // SwPageDesc is not in this SwPageDescs list!
+ assert( x->m_pdList == this );
+
+ iterator const ret = find_( x->GetName() );
+ if (ret != end())
+ m_PosIndex.erase( ret );
+ else
+ SAL_WARN( "sw", "SwPageDesc is not in SwPageDescs m_pdList!" );
+ x->m_pdList = nullptr;
+}
+
+void SwPageDescs::erase( const_iterator const& position )
+{
+ // SwPageDesc is not in this SwPageDescs list!
+ assert( (*position)->m_pdList == this );
+
+ (*position)->m_pdList = nullptr;
+ m_PosIndex.erase( position );
+}
+
+void SwPageDescs::erase( size_type index_ )
+{
+ erase( begin() + index_ );
+}
+
+void SwPageDescs::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPageDescs"));
+
+ for (const auto& pPageDesc : m_PosIndex)
+ {
+ pPageDesc->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/paintfrm.cxx b/sw/source/core/layout/paintfrm.cxx
new file mode 100644
index 000000000..f168cbd5a
--- /dev/null
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -0,0 +1,7721 @@
+/* -*- 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 <vcl/lazydelete.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/progress.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/prntitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <svx/ctredlin.hxx>
+#include <svx/framelink.hxx>
+#include <drawdoc.hxx>
+#include <tgrditem.hxx>
+#include <calbck.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtclds.hxx>
+#include <strings.hrc>
+#include <swmodule.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <section.hxx>
+#include <sectfrm.hxx>
+#include <viewimp.hxx>
+#include <dflyobj.hxx>
+#include <flyfrm.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <viewopt.hxx>
+#include <dview.hxx>
+#include <dcontact.hxx>
+#include <txtfrm.hxx>
+#include <ftnfrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <notxtfrm.hxx>
+#include <layact.hxx>
+#include <pagedesc.hxx>
+#include <ptqueue.hxx>
+#include <noteurl.hxx>
+#include "virtoutp.hxx"
+#include <lineinfo.hxx>
+#include <dbg_lay.hxx>
+#include <docsh.hxx>
+#include <svx/svdogrp.hxx>
+#include <sortedobjs.hxx>
+#include <EnhancedPDFExportHelper.hxx>
+#include <bodyfrm.hxx>
+#include <hffrm.hxx>
+#include <colfrm.hxx>
+#include <sw_primitivetypes2d.hxx>
+#include <swfont.hxx>
+
+#include <svx/sdr/primitive2d/sdrframeborderprimitive2d.hxx>
+#include <svx/sdr/contact/viewobjectcontactredirector.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+
+#include <ndole.hxx>
+#include <PostItMgr.hxx>
+#include <FrameControlsManager.hxx>
+#include <vcl/settings.hxx>
+
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+
+#include <svtools/borderhelper.hxx>
+
+#include <bitmaps.hlst>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/xfillit0.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/color/bcolortools.hxx>
+#include <basegfx/utils/b2dclipstate.hxx>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <vector>
+#include <algorithm>
+#include <wrtsh.hxx>
+#include <edtwin.hxx>
+#include <view.hxx>
+#include <paintfrm.hxx>
+#include <textboxhelper.hxx>
+#include <o3tl/typed_flags_set.hxx>
+
+#include <vcl/BitmapTools.hxx>
+#include <comphelper/lok.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <vcl/GraphicLoader.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+
+using namespace ::editeng;
+using namespace ::com::sun::star;
+
+namespace {
+
+struct SwPaintProperties;
+
+//Class declaration; here because they are only used in this file
+enum class SubColFlags {
+ Page = 0x01, //Helplines of the page
+ Tab = 0x08, //Helplines inside tables
+ Fly = 0x10, //Helplines inside fly frames
+ Sect = 0x20, //Helplines inside sections
+};
+
+}
+
+namespace o3tl {
+ template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {};
+}
+
+namespace {
+
+// Classes collecting the border lines and help lines
+class SwLineRect : public SwRect
+{
+ Color m_aColor;
+ SvxBorderLineStyle m_nStyle;
+ const SwTabFrame* m_pTabFrame;
+ SubColFlags m_nSubColor; //colorize subsidiary lines
+ bool m_bPainted; //already painted?
+ sal_uInt8 m_nLock; //To distinguish the line and the hell layer.
+public:
+ SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
+ const SwTabFrame *pT , const SubColFlags nSCol );
+
+ const Color& GetColor() const { return m_aColor; }
+ SvxBorderLineStyle GetStyle() const { return m_nStyle; }
+ const SwTabFrame* GetTab() const { return m_pTabFrame; }
+ void SetPainted() { m_bPainted = true; }
+ void Lock(bool bLock)
+ {
+ if (bLock)
+ ++m_nLock;
+ else if (m_nLock)
+ --m_nLock;
+ }
+ bool IsPainted() const { return m_bPainted; }
+ bool IsLocked() const { return m_nLock != 0; }
+ SubColFlags GetSubColor() const { return m_nSubColor; }
+
+ bool MakeUnion(const SwRect& rRect, SwPaintProperties const& properties);
+};
+
+}
+
+#ifdef IOS
+static void dummy_function()
+{
+ pid_t pid = getpid();
+ (void) pid;
+}
+#endif
+
+namespace {
+
+class SwLineRects
+{
+public:
+ std::vector<SwLineRect> m_aLineRects;
+ typedef std::vector< SwLineRect >::const_iterator const_iterator;
+ typedef std::vector< SwLineRect >::iterator iterator;
+ typedef std::vector< SwLineRect >::reverse_iterator reverse_iterator;
+ typedef std::vector< SwLineRect >::size_type size_type;
+ size_t m_nLastCount; //avoid unnecessary cycles in PaintLines
+ SwLineRects()
+ : m_nLastCount(0)
+ {
+#ifdef IOS
+ // Work around what is either a compiler bug in Xcode 5.1.1,
+ // or some unknown problem in this file. If I ifdef out this
+ // call, I get a crash in SwSubsRects::PaintSubsidiary: the
+ // address of the rLi reference variable is claimed to be
+ // 0x4000000!
+ dummy_function();
+#endif
+ }
+ void AddLineRect( const SwRect& rRect, const Color *pColor, const SvxBorderLineStyle nStyle,
+ const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const &properties );
+ void ConnectEdges( OutputDevice const *pOut, SwPaintProperties const &properties );
+ void PaintLines ( OutputDevice *pOut, SwPaintProperties const &properties );
+ void LockLines( bool bLock );
+
+ //Limit lines to 100
+ bool isFull() const { return m_aLineRects.size() > 100; }
+};
+
+class SwSubsRects : public SwLineRects
+{
+ void RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const &properties );
+public:
+ void PaintSubsidiary( OutputDevice *pOut, const SwLineRects *pRects, SwPaintProperties const &properties );
+};
+
+class BorderLines
+{
+ drawinglayer::primitive2d::Primitive2DContainer m_Lines;
+public:
+ void AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer);
+ drawinglayer::primitive2d::Primitive2DContainer GetBorderLines_Clear()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer lines;
+ lines.swap(m_Lines);
+ return lines;
+ }
+};
+
+}
+
+// Default zoom factor
+const double aEdgeScale = 0.5;
+
+//To optimize the expensive RetouchColor determination
+Color aGlobalRetoucheColor;
+
+namespace sw
+{
+Color* GetActiveRetoucheColor()
+{
+ return &aGlobalRetoucheColor;
+}
+}
+
+namespace {
+
+/**
+ * Container for static properties
+ */
+struct SwPaintProperties {
+ // Only repaint the Fly content as well as the background of the Fly content if
+ // a metafile is taken of the Fly.
+ bool bSFlyMetafile;
+ VclPtr<OutputDevice> pSFlyMetafileOut;
+ SwViewShell *pSGlobalShell;
+
+ // Retouch for transparent Flys is done by the background of the Flys.
+ // The Fly itself should certainly not be spared out. See PaintSwFrameBackground and
+ // lcl_SubtractFlys()
+ SwFlyFrame *pSRetoucheFly;
+ SwFlyFrame *pSRetoucheFly2;
+ SwFlyFrame *pSFlyOnlyDraw;
+
+ // The borders will be collected in pSLines during the Paint and later
+ // possibly merge them.
+ // The help lines will be collected and merged in gProp.pSSubsLines. These will
+ // be compared with pSLines before the work in order to avoid help lines
+ // to hide borders.
+ std::unique_ptr<BorderLines> pBLines;
+ std::unique_ptr<SwLineRects> pSLines;
+ std::unique_ptr<SwSubsRects> pSSubsLines;
+
+ // global variable for sub-lines of body, header, footer, section and footnote frames.
+ std::unique_ptr<SwSubsRects> pSSpecSubsLines;
+ SfxProgress *pSProgress;
+
+ // Sizes of a pixel and the corresponding halves. Will be reset when
+ // entering SwRootFrame::PaintSwFrame
+ tools::Long nSPixelSzW;
+ tools::Long nSPixelSzH;
+ tools::Long nSHalfPixelSzW;
+ tools::Long nSHalfPixelSzH;
+ tools::Long nSMinDistPixelW;
+ tools::Long nSMinDistPixelH;
+
+ Color aSGlobalRetoucheColor;
+
+ // Current zoom factor
+ double aSScaleX;
+ double aSScaleY;
+
+ SwPaintProperties()
+ : bSFlyMetafile(false)
+ , pSFlyMetafileOut(nullptr)
+ , pSGlobalShell(nullptr)
+ , pSRetoucheFly(nullptr)
+ , pSRetoucheFly2(nullptr)
+ , pSFlyOnlyDraw(nullptr)
+ , pSProgress(nullptr)
+ , nSPixelSzW(0)
+ , nSPixelSzH(0)
+ , nSHalfPixelSzW(0)
+ , nSHalfPixelSzH(0)
+ , nSMinDistPixelW(0)
+ , nSMinDistPixelH(0)
+ , aSScaleX(1)
+ , aSScaleY(1)
+ {
+ }
+
+};
+
+}
+
+static SwPaintProperties gProp;
+
+static bool isSubsidiaryLinesFlysEnabled()
+{
+ return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
+ SwViewOption::IsObjectBoundaries();
+}
+//other subsidiary lines enabled?
+static bool isSubsidiaryLinesEnabled()
+{
+ return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsWhitespaceHidden() &&
+ SwViewOption::IsDocBoundaries();
+}
+//subsidiary lines for sections
+static bool isSubsidiaryLinesForSectionsEnabled()
+{
+ return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
+ SwViewOption::IsSectionBoundaries();
+}
+
+
+namespace {
+
+bool isTableBoundariesEnabled()
+{
+ if (!gProp.pSGlobalShell->GetViewOptions()->IsTable())
+ return false;
+
+ if (gProp.pSGlobalShell->GetViewOptions()->IsPagePreview())
+ return false;
+
+ if (gProp.pSGlobalShell->GetViewOptions()->IsReadonly())
+ return false;
+
+ if (gProp.pSGlobalShell->GetViewOptions()->IsFormView())
+ return false;
+
+ return SwViewOption::IsTableBoundaries();
+}
+
+}
+
+/**
+ * Set borders alignment statics
+ * Adjustment for 'small' twip-to-pixel relations:
+ * For 'small' twip-to-pixel relations (less than 2:1)
+ * values of <gProp.nSHalfPixelSzW> and <gProp.nSHalfPixelSzH> are set to ZERO
+ */
+void SwCalcPixStatics( vcl::RenderContext const *pOut )
+{
+ // determine 'small' twip-to-pixel relation
+ bool bSmallTwipToPxRelW = false;
+ bool bSmallTwipToPxRelH = false;
+ {
+ Size aCheckTwipToPxRelSz( pOut->PixelToLogic( Size( 100, 100 )) );
+ if ( (aCheckTwipToPxRelSz.Width()/100.0) < 2.0 )
+ {
+ bSmallTwipToPxRelW = true;
+ }
+ if ( (aCheckTwipToPxRelSz.Height()/100.0) < 2.0 )
+ {
+ bSmallTwipToPxRelH = true;
+ }
+ }
+
+ Size aSz( pOut->PixelToLogic( Size( 1,1 )) );
+
+ gProp.nSPixelSzW = aSz.Width();
+ if( !gProp.nSPixelSzW )
+ gProp.nSPixelSzW = 1;
+ gProp.nSPixelSzH = aSz.Height();
+ if( !gProp.nSPixelSzH )
+ gProp.nSPixelSzH = 1;
+
+ // consider 'small' twip-to-pixel relations
+ if ( !bSmallTwipToPxRelW )
+ {
+ gProp.nSHalfPixelSzW = gProp.nSPixelSzW / 2 + 1;
+ }
+ else
+ {
+ gProp.nSHalfPixelSzW = 0;
+ }
+ // consider 'small' twip-to-pixel relations
+ if ( !bSmallTwipToPxRelH )
+ {
+ gProp.nSHalfPixelSzH = gProp.nSPixelSzH / 2 + 1;
+ }
+ else
+ {
+ gProp.nSHalfPixelSzH = 0;
+ }
+
+ gProp.nSMinDistPixelW = gProp.nSPixelSzW * 2 + 1;
+ gProp.nSMinDistPixelH = gProp.nSPixelSzH * 2 + 1;
+
+ const MapMode &rMap = pOut->GetMapMode();
+ gProp.aSScaleX = double(rMap.GetScaleX());
+ gProp.aSScaleY = double(rMap.GetScaleY());
+}
+
+namespace {
+
+/**
+ * To be able to save the statics so the paint is more or less reentrant
+ */
+class SwSavePaintStatics : public SwPaintProperties
+{
+public:
+ SwSavePaintStatics();
+ ~SwSavePaintStatics();
+};
+
+}
+
+SwSavePaintStatics::SwSavePaintStatics()
+{
+ // Saving globales
+ bSFlyMetafile = gProp.bSFlyMetafile;
+ pSGlobalShell = gProp.pSGlobalShell;
+ pSFlyMetafileOut = gProp.pSFlyMetafileOut;
+ pSRetoucheFly = gProp.pSRetoucheFly;
+ pSRetoucheFly2 = gProp.pSRetoucheFly2;
+ pSFlyOnlyDraw = gProp.pSFlyOnlyDraw;
+ pBLines = std::move(gProp.pBLines);
+ pSLines = std::move(gProp.pSLines);
+ pSSubsLines = std::move(gProp.pSSubsLines);
+ pSSpecSubsLines = std::move(gProp.pSSpecSubsLines);
+ pSProgress = gProp.pSProgress;
+ nSPixelSzW = gProp.nSPixelSzW;
+ nSPixelSzH = gProp.nSPixelSzH;
+ nSHalfPixelSzW = gProp.nSHalfPixelSzW;
+ nSHalfPixelSzH = gProp.nSHalfPixelSzH;
+ nSMinDistPixelW = gProp.nSMinDistPixelW;
+ nSMinDistPixelH = gProp.nSMinDistPixelH ;
+ aSGlobalRetoucheColor = aGlobalRetoucheColor;
+ aSScaleX = gProp.aSScaleX;
+ aSScaleY = gProp.aSScaleY;
+
+ // Restoring globales to default
+ gProp.bSFlyMetafile = false;
+ gProp.pSFlyMetafileOut = nullptr;
+ gProp.pSRetoucheFly = nullptr;
+ gProp.pSRetoucheFly2 = nullptr;
+ gProp.nSPixelSzW = gProp.nSPixelSzH =
+ gProp.nSHalfPixelSzW = gProp.nSHalfPixelSzH =
+ gProp.nSMinDistPixelW = gProp.nSMinDistPixelH = 0;
+ gProp.aSScaleX = gProp.aSScaleY = 1.0;
+ gProp.pSProgress = nullptr;
+}
+
+SwSavePaintStatics::~SwSavePaintStatics()
+{
+ // Restoring globales to saved one
+ gProp.pSGlobalShell = pSGlobalShell;
+ gProp.bSFlyMetafile = bSFlyMetafile;
+ gProp.pSFlyMetafileOut = pSFlyMetafileOut;
+ gProp.pSRetoucheFly = pSRetoucheFly;
+ gProp.pSRetoucheFly2 = pSRetoucheFly2;
+ gProp.pSFlyOnlyDraw = pSFlyOnlyDraw;
+ gProp.pBLines = std::move(pBLines);
+ gProp.pSLines = std::move(pSLines);
+ gProp.pSSubsLines = std::move(pSSubsLines);
+ gProp.pSSpecSubsLines = std::move(pSSpecSubsLines);
+ gProp.pSProgress = pSProgress;
+ gProp.nSPixelSzW = nSPixelSzW;
+ gProp.nSPixelSzH = nSPixelSzH;
+ gProp.nSHalfPixelSzW = nSHalfPixelSzW;
+ gProp.nSHalfPixelSzH = nSHalfPixelSzH;
+ gProp.nSMinDistPixelW = nSMinDistPixelW;
+ gProp.nSMinDistPixelH = nSMinDistPixelH;
+ aGlobalRetoucheColor = aSGlobalRetoucheColor;
+ gProp.aSScaleX = aSScaleX;
+ gProp.aSScaleY = aSScaleY;
+}
+
+void BorderLines::AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer)
+{
+ if(!rContainer.empty())
+ {
+ m_Lines.append(std::move(rContainer));
+ }
+}
+
+SwLineRect::SwLineRect(const SwRect& rRect, const Color* pCol, const SvxBorderLineStyle nStyl,
+ const SwTabFrame* pT, const SubColFlags nSCol)
+ : SwRect(rRect)
+ , m_nStyle(nStyl)
+ , m_pTabFrame(pT)
+ , m_nSubColor(nSCol)
+ , m_bPainted(false)
+ , m_nLock(0)
+{
+ if ( pCol != nullptr )
+ m_aColor = *pCol;
+}
+
+bool SwLineRect::MakeUnion( const SwRect &rRect, SwPaintProperties const & properties)
+{
+ // It has already been tested outside, whether the rectangles have
+ // the same orientation (horizontal or vertical), color, etc.
+ if ( Height() > Width() ) //Vertical line
+ {
+ if ( Left() == rRect.Left() && Width() == rRect.Width() )
+ {
+ // Merge when there is no gap between the lines
+ const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
+ if ( Bottom() + nAdd >= rRect.Top() &&
+ Top() - nAdd <= rRect.Bottom() )
+ {
+ Bottom( std::max( Bottom(), rRect.Bottom() ) );
+ Top ( std::min( Top(), rRect.Top() ) );
+ return true;
+ }
+ }
+ }
+ else
+ {
+ if ( Top() == rRect.Top() && Height() == rRect.Height() )
+ {
+ // Merge when there is no gap between the lines
+ const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
+ if ( Right() + nAdd >= rRect.Left() &&
+ Left() - nAdd <= rRect.Right() )
+ {
+ Right( std::max( Right(), rRect.Right() ) );
+ Left ( std::min( Left(), rRect.Left() ) );
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void SwLineRects::AddLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
+ const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const & properties )
+{
+ // Loop backwards because lines which can be combined, can usually be painted
+ // in the same context
+ for (reverse_iterator it = m_aLineRects.rbegin(); it != m_aLineRects.rend(); ++it)
+ {
+ SwLineRect &rLRect = *it;
+ // Test for the orientation, color, table
+ if ( rLRect.GetTab() == pTab &&
+ !rLRect.IsPainted() && rLRect.GetSubColor() == nSCol &&
+ (rLRect.Height() > rLRect.Width()) == (rRect.Height() > rRect.Width()) &&
+ (pCol && rLRect.GetColor() == *pCol) )
+ {
+ if ( rLRect.MakeUnion( rRect, properties ) )
+ return;
+ }
+ }
+ m_aLineRects.emplace_back(rRect, pCol, nStyle, pTab, nSCol);
+}
+
+void SwLineRects::ConnectEdges( OutputDevice const *pOut, SwPaintProperties const & properties )
+{
+ if ( pOut->GetOutDevType() != OUTDEV_PRINTER )
+ {
+ // I'm not doing anything for a too small zoom
+ if ( properties.aSScaleX < aEdgeScale || properties.aSScaleY < aEdgeScale )
+ return;
+ }
+
+ static const tools::Long nAdd = 20;
+
+ std::vector<SwLineRect*> aCheck;
+
+ for (size_t i = 0; i < m_aLineRects.size(); ++i)
+ {
+ SwLineRect& rL1 = m_aLineRects[i];
+ if ( !rL1.GetTab() || rL1.IsPainted() || rL1.IsLocked() )
+ continue;
+
+ aCheck.clear();
+
+ const bool bVert = rL1.Height() > rL1.Width();
+ tools::Long nL1a, nL1b, nL1c, nL1d;
+
+ if ( bVert )
+ {
+ nL1a = rL1.Top(); nL1b = rL1.Left();
+ nL1c = rL1.Right(); nL1d = rL1.Bottom();
+ }
+ else
+ {
+ nL1a = rL1.Left(); nL1b = rL1.Top();
+ nL1c = rL1.Bottom(); nL1d = rL1.Right();
+ }
+
+ // Collect all lines to possibly link with i1
+ for (iterator it2 = m_aLineRects.begin(); it2 != m_aLineRects.end(); ++it2)
+ {
+ SwLineRect &rL2 = *it2;
+ if ( rL2.GetTab() != rL1.GetTab() ||
+ rL2.IsPainted() ||
+ rL2.IsLocked() ||
+ (bVert == (rL2.Height() > rL2.Width())) )
+ continue;
+
+ tools::Long nL2a, nL2b, nL2c, nL2d;
+ if ( bVert )
+ {
+ nL2a = rL2.Top(); nL2b = rL2.Left();
+ nL2c = rL2.Right(); nL2d = rL2.Bottom();
+ }
+ else
+ {
+ nL2a = rL2.Left(); nL2b = rL2.Top();
+ nL2c = rL2.Bottom(); nL2d = rL2.Right();
+ }
+
+ if ( (nL1a - nAdd < nL2d && nL1d + nAdd > nL2a) &&
+ ((nL1b > nL2b && nL1c < nL2c) ||
+ (nL1c >= nL2c && nL1b - nAdd < nL2c) ||
+ (nL1b <= nL2b && nL1c + nAdd > nL2b)) )
+ {
+ aCheck.push_back( &rL2 );
+ }
+ }
+ if ( aCheck.size() < 2 )
+ continue;
+
+ bool bRemove = false;
+
+ // For each line test all following ones.
+ for ( size_t k = 0; !bRemove && k < aCheck.size(); ++k )
+ {
+ SwLineRect &rR1 = *aCheck[k];
+
+ for ( size_t k2 = k+1; !bRemove && k2 < aCheck.size(); ++k2 )
+ {
+ SwLineRect &rR2 = *aCheck[k2];
+ if ( bVert )
+ {
+ SwLineRect *pLA = nullptr;
+ SwLineRect *pLB = nullptr;
+ if ( rR1.Top() < rR2.Top() )
+ {
+ pLA = &rR1; pLB = &rR2;
+ }
+ else if ( rR1.Top() > rR2.Top() )
+ {
+ pLA = &rR2; pLB = &rR1;
+ }
+ // are k1 and k2 describing a double line?
+ if ( pLA && pLA->Bottom() + 60 > pLB->Top() )
+ {
+ if ( rL1.Top() < pLA->Top() )
+ {
+ if ( rL1.Bottom() == pLA->Bottom() )
+ continue; //Small mistake (where?)
+
+ SwRect aIns( rL1 );
+ aIns.Bottom( pLA->Bottom() );
+ if ( !rL1.Contains( aIns ) )
+ continue;
+ m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
+ SvxBorderLineStyle::SOLID, rL1.GetTab(),
+ SubColFlags::Tab);
+ if ( isFull() )
+ {
+ --i;
+ k = aCheck.size();
+ break;
+ }
+ }
+
+ if ( rL1.Bottom() > pLB->Bottom() )
+ rL1.Top( pLB->Top() ); // extend i1 on the top
+ else
+ bRemove = true; //stopping, remove i1
+ }
+ }
+ else
+ {
+ SwLineRect *pLA = nullptr;
+ SwLineRect *pLB = nullptr;
+ if ( rR1.Left() < rR2.Left() )
+ {
+ pLA = &rR1; pLB = &rR2;
+ }
+ else if ( rR1.Left() > rR2.Left() )
+ {
+ pLA = &rR2; pLB = &rR1;
+ }
+ // Is it double line?
+ if ( pLA && pLA->Right() + 60 > pLB->Left() )
+ {
+ if ( rL1.Left() < pLA->Left() )
+ {
+ if ( rL1.Right() == pLA->Right() )
+ continue; //small error
+
+ SwRect aIns( rL1 );
+ aIns.Right( pLA->Right() );
+ if ( !rL1.Contains( aIns ) )
+ continue;
+ m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
+ SvxBorderLineStyle::SOLID, rL1.GetTab(),
+ SubColFlags::Tab);
+ if ( isFull() )
+ {
+ --i;
+ k = aCheck.size();
+ break;
+ }
+ }
+ if ( rL1.Right() > pLB->Right() )
+ rL1.Left( pLB->Left() );
+ else
+ bRemove = true;
+ }
+ }
+ }
+ }
+ if ( bRemove )
+ {
+ m_aLineRects.erase(m_aLineRects.begin() + i);
+ --i;
+ }
+ }
+}
+
+void SwSubsRects::RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const & properties )
+{
+ // All help lines that are covered by any border will be removed or split
+ for (size_t i = 0; i < m_aLineRects.size(); ++i)
+ {
+ // get a copy instead of a reference, because an <insert> may destroy
+ // the object due to a necessary array resize.
+ const SwLineRect aSubsLineRect(m_aLineRects[i]);
+
+ // add condition <aSubsLineRect.IsLocked()> in order to consider only
+ // border lines, which are *not* locked.
+ if ( aSubsLineRect.IsPainted() ||
+ aSubsLineRect.IsLocked() )
+ continue;
+
+ const bool bVerticalSubs = aSubsLineRect.Height() > aSubsLineRect.Width();
+ SwRect aSubsRect( aSubsLineRect );
+ if ( bVerticalSubs )
+ {
+ aSubsRect.AddLeft ( - (properties.nSPixelSzW+properties.nSHalfPixelSzW) );
+ aSubsRect.AddRight ( properties.nSPixelSzW+properties.nSHalfPixelSzW );
+ }
+ else
+ {
+ aSubsRect.AddTop ( - (properties.nSPixelSzH+properties.nSHalfPixelSzH) );
+ aSubsRect.AddBottom( properties.nSPixelSzH+properties.nSHalfPixelSzH );
+ }
+ for (const_iterator itK = rRects.m_aLineRects.begin(); itK != rRects.m_aLineRects.end();
+ ++itK)
+ {
+ const SwLineRect &rLine = *itK;
+
+ // do *not* consider painted or locked border lines.
+ // #i1837# - locked border lines have to be considered.
+ if ( rLine.IsLocked () )
+ continue;
+
+ if ( !bVerticalSubs == ( rLine.Height() > rLine.Width() ) ) //same direction?
+ continue;
+
+ if ( aSubsRect.Overlaps( rLine ) )
+ {
+ if ( bVerticalSubs ) // Vertical?
+ {
+ if ( aSubsRect.Left() <= rLine.Right() &&
+ aSubsRect.Right() >= rLine.Left() )
+ {
+ tools::Long nTmp = rLine.Top()-(properties.nSPixelSzH+1);
+ if ( aSubsLineRect.Top() < nTmp )
+ {
+ SwRect aNewSubsRect( aSubsLineRect );
+ aNewSubsRect.Bottom( nTmp );
+ m_aLineRects.emplace_back(aNewSubsRect, nullptr,
+ aSubsLineRect.GetStyle(), nullptr,
+ aSubsLineRect.GetSubColor());
+ }
+ nTmp = rLine.Bottom()+properties.nSPixelSzH+1;
+ if ( aSubsLineRect.Bottom() > nTmp )
+ {
+ SwRect aNewSubsRect( aSubsLineRect );
+ aNewSubsRect.Top( nTmp );
+ m_aLineRects.emplace_back(aNewSubsRect, nullptr,
+ aSubsLineRect.GetStyle(), nullptr,
+ aSubsLineRect.GetSubColor());
+ }
+ m_aLineRects.erase(m_aLineRects.begin() + i);
+ --i;
+ break;
+ }
+ }
+ else // Horizontal
+ {
+ if ( aSubsRect.Top() <= rLine.Bottom() &&
+ aSubsRect.Bottom() >= rLine.Top() )
+ {
+ tools::Long nTmp = rLine.Left()-(properties.nSPixelSzW+1);
+ if ( aSubsLineRect.Left() < nTmp )
+ {
+ SwRect aNewSubsRect( aSubsLineRect );
+ aNewSubsRect.Right( nTmp );
+ m_aLineRects.emplace_back(aNewSubsRect, nullptr,
+ aSubsLineRect.GetStyle(), nullptr,
+ aSubsLineRect.GetSubColor());
+ }
+ nTmp = rLine.Right()+properties.nSPixelSzW+1;
+ if ( aSubsLineRect.Right() > nTmp )
+ {
+ SwRect aNewSubsRect( aSubsLineRect );
+ aNewSubsRect.Left( nTmp );
+ m_aLineRects.emplace_back(aNewSubsRect, nullptr,
+ aSubsLineRect.GetStyle(), nullptr,
+ aSubsLineRect.GetSubColor());
+ }
+ m_aLineRects.erase(m_aLineRects.begin() + i);
+ --i;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void SwLineRects::LockLines( bool bLock )
+{
+ for (SwLineRect& rLRect : m_aLineRects)
+ rLRect.Lock(bLock);
+}
+
+static void lcl_DrawDashedRect( OutputDevice * pOut, SwLineRect const & rLRect )
+{
+ tools::Long startX = rLRect.Left( ), endX;
+ tools::Long startY = rLRect.Top( ), endY;
+
+ // Discriminate vertically stretched rect from horizontally stretched
+ // and restrict minimum nHalfLWidth to 1
+ tools::Long nHalfLWidth = std::max( std::min( rLRect.Width( ), rLRect.Height( ) ) / 2, tools::Long(1) );
+
+ if ( rLRect.Height( ) > rLRect.Width( ) )
+ {
+ startX += nHalfLWidth;
+ endX = startX;
+ endY = startY + rLRect.Height( );
+ }
+ else
+ {
+ startY += nHalfLWidth;
+ endY = startY;
+ endX = startX + rLRect.Width( );
+ }
+
+ svtools::DrawLine( *pOut, Point( startX, startY ), Point( endX, endY ),
+ sal_uInt32( nHalfLWidth * 2 ), rLRect.GetStyle( ) );
+}
+
+void SwLineRects::PaintLines( OutputDevice *pOut, SwPaintProperties const &properties )
+{
+ // Paint the borders. Sadly two passes are needed.
+ // Once for the inside and once for the outside edges of tables
+ if (m_aLineRects.size() == m_nLastCount)
+ return;
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
+
+ pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ pOut->SetFillColor();
+ pOut->SetLineColor();
+ ConnectEdges( pOut, properties );
+ const Color *pLast = nullptr;
+
+ bool bPaint2nd = false;
+ size_t nMinCount = m_aLineRects.size();
+
+ for (size_t i = 0; i < m_aLineRects.size(); ++i)
+ {
+ SwLineRect& rLRect = m_aLineRects[i];
+
+ if ( rLRect.IsPainted() )
+ continue;
+
+ if ( rLRect.IsLocked() )
+ {
+ nMinCount = std::min( nMinCount, i );
+ continue;
+ }
+
+ // Paint it now or in the second pass?
+ bool bPaint = true;
+ if ( rLRect.GetTab() )
+ {
+ if ( rLRect.Height() > rLRect.Width() )
+ {
+ // Vertical edge, overlapping with the table edge?
+ SwTwips nLLeft = rLRect.Left() - 30,
+ nLRight = rLRect.Right() + 30,
+ nTLeft = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Left(),
+ nTRight = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Right();
+ if ( (nTLeft >= nLLeft && nTLeft <= nLRight) ||
+ (nTRight>= nLLeft && nTRight<= nLRight) )
+ bPaint = false;
+ }
+ else
+ {
+ // Horizontal edge, overlapping with the table edge?
+ SwTwips nLTop = rLRect.Top() - 30,
+ nLBottom = rLRect.Bottom() + 30,
+ nTTop = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Top(),
+ nTBottom = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Bottom();
+ if ( (nTTop >= nLTop && nTTop <= nLBottom) ||
+ (nTBottom >= nLTop && nTBottom <= nLBottom) )
+ bPaint = false;
+ }
+ }
+ if ( bPaint )
+ {
+ if ( !pLast || *pLast != rLRect.GetColor() )
+ {
+ pLast = &rLRect.GetColor();
+
+ DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
+ if( properties.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ pOut->SetDrawMode( DrawModeFlags::Default );
+
+ pOut->SetLineColor( *pLast );
+ pOut->SetFillColor( *pLast );
+ pOut->SetDrawMode( nOldDrawMode );
+ }
+
+ if( !rLRect.IsEmpty() )
+ lcl_DrawDashedRect( pOut, rLRect );
+ rLRect.SetPainted();
+ }
+ else
+ bPaint2nd = true;
+ }
+ if ( bPaint2nd )
+ {
+ for (size_t i = 0; i < m_aLineRects.size(); ++i)
+ {
+ SwLineRect& rLRect = m_aLineRects[i];
+ if ( rLRect.IsPainted() )
+ continue;
+
+ if ( rLRect.IsLocked() )
+ {
+ nMinCount = std::min( nMinCount, i );
+ continue;
+ }
+
+ if ( !pLast || *pLast != rLRect.GetColor() )
+ {
+ pLast = &rLRect.GetColor();
+
+ DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
+ if( properties.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ pOut->SetDrawMode( DrawModeFlags::Default );
+ }
+
+ pOut->SetFillColor( *pLast );
+ pOut->SetDrawMode( nOldDrawMode );
+ }
+ if( !rLRect.IsEmpty() )
+ lcl_DrawDashedRect( pOut, rLRect );
+ rLRect.SetPainted();
+ }
+ }
+ m_nLastCount = nMinCount;
+ pOut->Pop();
+
+}
+
+void SwSubsRects::PaintSubsidiary( OutputDevice *pOut,
+ const SwLineRects *pRects,
+ SwPaintProperties const & properties )
+{
+ if (m_aLineRects.empty())
+ return;
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
+
+ // Remove all help line that are almost covered (tables)
+ for (size_type i = 0; i != m_aLineRects.size(); ++i)
+ {
+ SwLineRect& rLi = m_aLineRects[i];
+ const bool bVerticalSubs = rLi.Height() > rLi.Width();
+
+ for (size_type k = i + 1; k != m_aLineRects.size(); ++k)
+ {
+ SwLineRect& rLk = m_aLineRects[k];
+ if ( rLi.SSize() == rLk.SSize() )
+ {
+ if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) )
+ {
+ if ( bVerticalSubs )
+ {
+ tools::Long nLi = rLi.Right();
+ tools::Long nLk = rLk.Right();
+ if ( rLi.Top() == rLk.Top() &&
+ ((nLi < rLk.Left() && nLi+21 > rLk.Left()) ||
+ (nLk < rLi.Left() && nLk+21 > rLi.Left())))
+ {
+ m_aLineRects.erase(m_aLineRects.begin() + i);
+ // don't continue with inner loop any more:
+ // the array may shrink!
+ --i;
+ break;
+ }
+ }
+ else
+ {
+ tools::Long nLi = rLi.Bottom();
+ tools::Long nLk = rLk.Bottom();
+ if ( rLi.Left() == rLk.Left() &&
+ ((nLi < rLk.Top() && nLi+21 > rLk.Top()) ||
+ (nLk < rLi.Top() && nLk+21 > rLi.Top())))
+ {
+ m_aLineRects.erase(m_aLineRects.begin() + i);
+ // don't continue with inner loop any more:
+ // the array may shrink!
+ --i;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (pRects && (!pRects->m_aLineRects.empty()))
+ RemoveSuperfluousSubsidiaryLines( *pRects, properties );
+
+ if (m_aLineRects.empty())
+ return;
+
+ pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ pOut->SetLineColor();
+
+ // Reset draw mode in high contrast mode in order to get fill color
+ // set at output device. Recover draw mode after draw of lines.
+ // Necessary for the subsidiary lines painted by the fly frames.
+ DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
+ if( gProp.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ pOut->SetDrawMode( DrawModeFlags::Default );
+ }
+
+ for (SwLineRect& rLRect : m_aLineRects)
+ {
+ // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines.
+ if ( !rLRect.IsPainted() &&
+ !rLRect.IsLocked() )
+ {
+ const Color *pCol = nullptr;
+ switch ( rLRect.GetSubColor() )
+ {
+ case SubColFlags::Page: pCol = &SwViewOption::GetDocBoundariesColor(); break;
+ case SubColFlags::Fly: pCol = &SwViewOption::GetObjectBoundariesColor(); break;
+ case SubColFlags::Tab: pCol = &SwViewOption::GetTableBoundariesColor(); break;
+ case SubColFlags::Sect: pCol = &SwViewOption::GetSectionBoundColor(); break;
+ }
+
+ if (pCol && pOut->GetFillColor() != *pCol)
+ pOut->SetFillColor( *pCol );
+ pOut->DrawRect( rLRect.SVRect() );
+
+ rLRect.SetPainted();
+ }
+ }
+
+ pOut->SetDrawMode( nOldDrawMode );
+
+ pOut->Pop();
+}
+
+// Various functions that are use in this file.
+
+/**
+ * Function <SwAlignRect(..)> is also used outside this file
+ *
+ * Correction: adjust rectangle on pixel level in order to make sure,
+ * that the border "leaves its original pixel", if it has to
+ * No prior adjustments for odd relation between pixel and twip
+ */
+void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext )
+{
+ if( !rRect.HasArea() )
+ return;
+
+ // Make sure that view shell (parameter <pSh>) exists, if the output device
+ // is taken from this view shell --> no output device, no alignment
+ // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set
+ if ( !gProp.bSFlyMetafile && !pSh )
+ {
+ return;
+ }
+
+ const vcl::RenderContext *pOut = gProp.bSFlyMetafile ?
+ gProp.pSFlyMetafileOut.get() : pRenderContext;
+
+ // Hold original rectangle in pixel
+ const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() );
+ // Determine pixel-center rectangle in twip
+ const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) );
+
+ // Perform adjustments on pixel level.
+ SwRect aAlignedPxRect( aOrgPxRect );
+ if ( rRect.Top() > aPxCenterRect.Top() )
+ {
+ // 'leave pixel overlapping on top'
+ aAlignedPxRect.AddTop( 1 );
+ }
+
+ if ( rRect.Bottom() < aPxCenterRect.Bottom() )
+ {
+ // 'leave pixel overlapping on bottom'
+ aAlignedPxRect.AddBottom( - 1 );
+ }
+
+ if ( rRect.Left() > aPxCenterRect.Left() )
+ {
+ // 'leave pixel overlapping on left'
+ aAlignedPxRect.AddLeft( 1 );
+ }
+
+ if ( rRect.Right() < aPxCenterRect.Right() )
+ {
+ // 'leave pixel overlapping on right'
+ aAlignedPxRect.AddRight( - 1 );
+ }
+
+ // Consider negative width/height check, if aligned SwRect has negative width/height.
+ // If Yes, adjust it to width/height = 0 twip.
+ // NOTE: A SwRect with negative width/height can occur, if the width/height
+ // of the given SwRect in twip was less than a pixel in twip and that
+ // the alignment calculates that the aligned SwRect should not contain
+ // the pixels the width/height is on.
+ if ( aAlignedPxRect.Width() < 0 )
+ {
+ aAlignedPxRect.Width(0);
+ }
+ if ( aAlignedPxRect.Height() < 0 )
+ {
+ aAlignedPxRect.Height(0);
+ }
+ // Consider zero width/height for converting a rectangle from
+ // pixel to logic it needs a width/height. Thus, set width/height
+ // to one, if it's zero and correct this on the twip level after the conversion.
+ bool bZeroWidth = false;
+ if ( aAlignedPxRect.Width() == 0 )
+ {
+ aAlignedPxRect.Width(1);
+ bZeroWidth = true;
+ }
+ bool bZeroHeight = false;
+ if ( aAlignedPxRect.Height() == 0 )
+ {
+ aAlignedPxRect.Height(1);
+ bZeroHeight = true;
+ }
+
+ rRect = SwRect(pOut->PixelToLogic( aAlignedPxRect.SVRect() ));
+
+ // Consider zero width/height and adjust calculated aligned twip rectangle.
+ // Reset width/height to zero; previous negative width/height haven't to be considered.
+ if ( bZeroWidth )
+ {
+ rRect.Width(0);
+ }
+ if ( bZeroHeight )
+ {
+ rRect.Height(0);
+ }
+}
+
+/**
+ * Method to pixel-align rectangle for drawing graphic object
+ *
+ * Because we are drawing graphics from the left-top-corner in conjunction
+ * with size coordinates, these coordinates have to be calculated at a pixel
+ * level.
+ * Thus, we convert the rectangle to pixel and then convert to left-top-corner
+ * and then get size of pixel rectangle back to logic.
+ * This calculation is necessary, because there's a different between
+ * the conversion from logic to pixel of a normal rectangle with its left-top-
+ * and right-bottom-corner and the same conversion of the same rectangle
+ * with left-top-corner and size.
+ *
+ * NOTE: Call this method before each <GraphicObject.Draw(...)>
+*/
+void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut )
+{
+ tools::Rectangle aPxRect = rOut.LogicToPixel( pGrfRect->SVRect() );
+ pGrfRect->Pos( rOut.PixelToLogic( aPxRect.TopLeft() ) );
+ pGrfRect->SSize( rOut.PixelToLogic( aPxRect.GetSize() ) );
+}
+
+static tools::Long lcl_AlignWidth( const tools::Long nWidth, SwPaintProperties const & properties )
+{
+ if ( nWidth )
+ {
+ const tools::Long nW = nWidth % properties.nSPixelSzW;
+
+ if ( !nW || nW > properties.nSHalfPixelSzW )
+ return std::max(tools::Long(1), nWidth - properties.nSHalfPixelSzW);
+ }
+ return nWidth;
+}
+
+static tools::Long lcl_AlignHeight( const tools::Long nHeight, SwPaintProperties const & properties )
+{
+ if ( nHeight )
+ {
+ const tools::Long nH = nHeight % properties.nSPixelSzH;
+
+ if ( !nH || nH > properties.nSHalfPixelSzH )
+ return std::max(tools::Long(1), nHeight - properties.nSHalfPixelSzH);
+ }
+ return nHeight;
+}
+
+/**
+ * Calculate PrtArea plus surrounding plus shadow
+ */
+static void lcl_CalcBorderRect( SwRect &rRect, const SwFrame *pFrame,
+ const SwBorderAttrs &rAttrs,
+ const bool bShadow,
+ SwPaintProperties const & properties)
+{
+ // Special handling for cell frames.
+ // The printing area of a cell frame is completely enclosed in the frame area
+ // and a cell frame has no shadow. Thus, for cell frames the calculated
+ // area equals the frame area.
+ // Notes: Borders of cell frames in R2L text direction will switch its side
+ // - left border is painted on the right; right border on the left.
+ // See <lcl_PaintLeftLine> and <lcl_PaintRightLine>.
+ if( pFrame->IsSctFrame() )
+ {
+ rRect = pFrame->getFramePrintArea();
+ rRect.Pos() += pFrame->getFrameArea().Pos();
+ }
+ else if ( pFrame->IsCellFrame() )
+ rRect = pFrame->getFrameArea();
+ else
+ {
+ rRect = pFrame->getFramePrintArea();
+ rRect.Pos() += pFrame->getFrameArea().Pos();
+
+ SwRectFn fnRect = pFrame->IsVertical() ? ( pFrame->IsVertLR() ? (pFrame->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+
+ const SvxBoxItem &rBox = rAttrs.GetBox();
+ const bool bTop = 0 != (pFrame->*fnRect->fnGetTopMargin)();
+ if ( bTop || rBox.GetTop() )
+ {
+ SwTwips nDiff = rBox.GetTop() ?
+ rBox.CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine=*/false, /*bAllowNegative=*/true ) :
+ rBox.GetDistance( SvxBoxItemLine::TOP );
+ if( nDiff )
+ (rRect.*fnRect->fnSubTop)( nDiff );
+ }
+
+ const bool bBottom = 0 != (pFrame->*fnRect->fnGetBottomMargin)();
+ if ( bBottom )
+ {
+ SwTwips nDiff = 0;
+ // #i29550#
+ if ( pFrame->IsTabFrame() &&
+ static_cast<const SwTabFrame*>(pFrame)->IsCollapsingBorders() )
+ {
+ // For collapsing borders, we have to add the height of
+ // the height of the last line
+ nDiff = static_cast<const SwTabFrame*>(pFrame)->GetBottomLineSize();
+ }
+ else
+ {
+ nDiff = rBox.GetBottom() ?
+ rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM ) :
+ rBox.GetDistance( SvxBoxItemLine::BOTTOM );
+ }
+ if( nDiff )
+ (rRect.*fnRect->fnAddBottom)( nDiff );
+ }
+
+ if ( rBox.GetLeft() )
+ (rRect.*fnRect->fnSubLeft)( rBox.CalcLineSpace( SvxBoxItemLine::LEFT ) );
+ else
+ (rRect.*fnRect->fnSubLeft)( rBox.GetDistance( SvxBoxItemLine::LEFT ) );
+
+ if ( rBox.GetRight() )
+ (rRect.*fnRect->fnAddRight)( rBox.CalcLineSpace( SvxBoxItemLine::RIGHT ) );
+ else
+ (rRect.*fnRect->fnAddRight)( rBox.GetDistance( SvxBoxItemLine::RIGHT ) );
+
+ if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
+ {
+ const SvxShadowItem &rShadow = rAttrs.GetShadow();
+ if ( bTop )
+ (rRect.*fnRect->fnSubTop)(rShadow.CalcShadowSpace(SvxShadowItemSide::TOP));
+ (rRect.*fnRect->fnSubLeft)(rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT));
+ if ( bBottom )
+ (rRect.*fnRect->fnAddBottom)
+ (rShadow.CalcShadowSpace( SvxShadowItemSide::BOTTOM ));
+ (rRect.*fnRect->fnAddRight)(rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT));
+ }
+ }
+
+ ::SwAlignRect( rRect, properties.pSGlobalShell, properties.pSGlobalShell ? properties.pSGlobalShell->GetOut() : nullptr );
+}
+
+/**
+ * Extend left/right border/shadow rectangle to bottom of previous frame/to
+ * top of next frame, if border/shadow is joined with previous/next frame
+ */
+static void lcl_ExtendLeftAndRight( SwRect& _rRect,
+ const SwFrame& _rFrame,
+ const SwBorderAttrs& _rAttrs,
+ const SwRectFn& _rRectFn )
+{
+ if ( _rAttrs.JoinedWithPrev( _rFrame ) )
+ {
+ const SwFrame* pPrevFrame = _rFrame.GetPrev();
+ (_rRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
+ }
+ if ( _rAttrs.JoinedWithNext( _rFrame ) )
+ {
+ const SwFrame* pNextFrame = _rFrame.GetNext();
+ (_rRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
+ }
+}
+
+/// Returns a range suitable for subtraction when lcl_SubtractFlys() is used.
+/// Otherwise DrawFillAttributes() expands the clip path itself.
+static basegfx::B2DRange lcl_ShrinkFly(const SwRect& rRect)
+{
+ static MapMode aMapMode(MapUnit::MapTwip);
+ static const Size aSingleUnit = Application::GetDefaultDevice()->PixelToLogic(Size(1, 1), aMapMode);
+
+ double x1 = rRect.Left() + aSingleUnit.getWidth();
+ double y1 = rRect.Top() + aSingleUnit.getHeight();
+ double x2 = rRect.Right() - aSingleUnit.getWidth();
+ double y2 = rRect.Bottom() - aSingleUnit.getHeight();
+
+ return basegfx::B2DRange(x1, y1, x2, y2);
+}
+
+static void lcl_SubtractFlys( const SwFrame *pFrame, const SwPageFrame *pPage,
+ const SwRect &rRect, SwRegionRects &rRegion, basegfx::utils::B2DClipState& rClipState, SwPaintProperties const & rProperties)
+{
+ const SwSortedObjs& rObjs = *pPage->GetSortedObjs();
+ const SwFlyFrame* pSelfFly = pFrame->IsInFly() ? pFrame->FindFlyFrame() : gProp.pSRetoucheFly2;
+ if (!gProp.pSRetoucheFly)
+ gProp.pSRetoucheFly = gProp.pSRetoucheFly2;
+
+ for (size_t j = 0; (j < rObjs.size()) && !rRegion.empty(); ++j)
+ {
+ const SwAnchoredObject* pAnchoredObj = rObjs[j];
+ const SdrObject* pSdrObj = pAnchoredObj->GetDrawObj();
+
+ // Do not consider invisible objects
+ if (!pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(pSdrObj->GetLayer()))
+ continue;
+
+ const SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
+ if (!pFly)
+ continue;
+
+ if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.Overlaps(pFly->getFrameArea()))
+ continue;
+
+ if (!pFly->GetFormat()->GetPrint().GetValue() &&
+ (OUTDEV_PRINTER == gProp.pSGlobalShell->GetOut()->GetOutDevType() ||
+ gProp.pSGlobalShell->IsPreview()))
+ continue;
+
+ const bool bLowerOfSelf = pSelfFly && pFly->IsLowerOf( pSelfFly );
+
+ //For character bound Flys only examine those Flys in which it is not
+ //anchored itself.
+ //Why only for character bound ones you may ask? It never makes sense to
+ //subtract frames in which it is anchored itself right?
+ if (pSelfFly && pSelfFly->IsLowerOf(pFly))
+ continue;
+
+ //Any why does it not apply for the RetoucheFly too?
+ if (gProp.pSRetoucheFly && gProp.pSRetoucheFly->IsLowerOf(pFly))
+ continue;
+
+#if OSL_DEBUG_LEVEL > 0
+ //Flys who are anchored inside their own one, must have a bigger OrdNum
+ //or be character bound.
+ if (pSelfFly && bLowerOfSelf)
+ {
+ OSL_ENSURE( pFly->IsFlyInContentFrame() ||
+ pSdrObj->GetOrdNumDirect() > pSelfFly->GetVirtDrawObj()->GetOrdNumDirect(),
+ "Fly with wrong z-Order" );
+ }
+#endif
+
+ bool bStopOnHell = true;
+ if (pSelfFly)
+ {
+ const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
+ if (pSdrObj->GetLayer() == pTmp->GetLayer())
+ {
+ if (pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect())
+ //In the same layer we only observe those that are above.
+ continue;
+ }
+ else
+ {
+ if (!bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue())
+ //From other layers we are only interested in non
+ //transparent ones or those that are internal
+ continue;
+ bStopOnHell = false;
+ }
+ }
+ if (gProp.pSRetoucheFly)
+ {
+ const SdrObject *pTmp = gProp.pSRetoucheFly->GetVirtDrawObj();
+ if ( pSdrObj->GetLayer() == pTmp->GetLayer() )
+ {
+ if ( pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
+ //In the same layer we only observe those that are above.
+ continue;
+ }
+ else
+ {
+ if (!pFly->IsLowerOf( gProp.pSRetoucheFly ) && !pFly->GetFormat()->GetOpaque().GetValue())
+ //From other layers we are only interested in non
+ //transparent ones or those that are internal
+ continue;
+ bStopOnHell = false;
+ }
+ }
+
+ //If the content of the Fly is transparent, we subtract it only if it's
+ //contained in the hell layer.
+ const IDocumentDrawModelAccess& rIDDMA = pFly->GetFormat()->getIDocumentDrawModelAccess();
+ bool bHell = pSdrObj->GetLayer() == rIDDMA.GetHellId();
+ if ( (bStopOnHell && bHell) ||
+ /// Change internal order of condition
+ /// first check "!bHell", then "..->Lower()" and "..->IsNoTextFrame()"
+ /// have not to be performed, if frame is in "Hell"
+ ( !bHell && pFly->Lower() && pFly->Lower()->IsNoTextFrame() &&
+ (static_cast<SwNoTextFrame const*>(pFly->Lower())->IsTransparent() ||
+ static_cast<SwNoTextFrame const*>(pFly->Lower())->HasAnimation() ||
+ pFly->GetFormat()->GetSurround().IsContour()
+ )
+ )
+ )
+ continue;
+
+ // Own if-statements for transparent background/shadow of fly frames
+ // in order to handle special conditions.
+ if (pFly->IsBackgroundTransparent())
+ {
+ // Background <pFly> is transparent drawn. Thus normally, its region
+ // have not to be subtracted from given region.
+ // But, if method is called for a fly frame and
+ // <pFly> is a direct lower of this fly frame and
+ // <pFly> inherites its transparent background brush from its parent,
+ // then <pFly> frame area have to be subtracted from given region.
+ // NOTE: Because in Status Quo transparent backgrounds can only be
+ // assigned to fly frames, the handle of this special case
+ // avoids drawing of transparent areas more than once, if
+ // a fly frame inherites a transparent background from its
+ // parent fly frame.
+ if (pFrame->IsFlyFrame() &&
+ (pFly->GetAnchorFrame()->FindFlyFrame() == pFrame) &&
+ pFly->GetFormat()->IsBackgroundBrushInherited()
+ )
+ {
+ SwRect aRect;
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
+ rRegion -= aRect;
+ rClipState.subtractRange(lcl_ShrinkFly(aRect));
+ continue;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ if (bHell && pFly->GetAnchorFrame()->IsInFly())
+ {
+ //So the border won't get dismantled by the background of the other
+ //Fly.
+ SwRect aRect;
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
+ rRegion -= aRect;
+ rClipState.subtractRange(lcl_ShrinkFly(aRect));
+ }
+ else
+ {
+ SwRect aRect( pFly->getFramePrintArea() );
+ aRect += pFly->getFrameArea().Pos();
+ rRegion -= aRect;
+ rClipState.subtractRange(lcl_ShrinkFly(aRect));
+ }
+ }
+ if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2)
+ gProp.pSRetoucheFly = nullptr;
+}
+
+static void lcl_implDrawGraphicBackground(const SvxBrushItem& _rBackgrdBrush,
+ vcl::RenderContext& _rOut,
+ const SwRect& _rAlignedPaintRect,
+ const GraphicObject& _rGraphicObj,
+ SwPaintProperties const & properties)
+{
+ /// determine color of background
+ /// If color of background brush is not "no fill"/"auto fill" or
+ /// <SwPaintProperties.bSFlyMetafile> is set, use color of background brush, otherwise
+ /// use global retouche color.
+ const Color aColor( ( (_rBackgrdBrush.GetColor() != COL_TRANSPARENT) || properties.bSFlyMetafile )
+ ? _rBackgrdBrush.GetColor()
+ : aGlobalRetoucheColor );
+
+ /// determine, if background color have to be drawn transparent
+ /// and calculate transparency percent value
+ sal_Int8 nTransparencyPercent = 0;
+ bool bDrawTransparent = false;
+ if ( aColor.IsTransparent() )
+ /// background color is transparent --> draw transparent.
+ {
+ bDrawTransparent = true;
+ nTransparencyPercent = ((255 - aColor.GetAlpha())*100 + 0x7F)/0xFF;
+ }
+ else if ( (_rGraphicObj.GetAttr().IsTransparent()) &&
+ (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) )
+ /// graphic is drawn transparent and background color is
+ /// "no fill"/"auto fill" --> draw transparent
+ {
+ bDrawTransparent = true;
+ nTransparencyPercent = 100 - (_rGraphicObj.GetAttr().GetAlpha() * 100 + 127) / 255;
+ }
+
+ if ( bDrawTransparent )
+ {
+ /// draw background transparent
+ if( _rOut.GetFillColor() != aColor.GetRGBColor() )
+ _rOut.SetFillColor( aColor.GetRGBColor() );
+ tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() );
+ _rOut.DrawTransparent( aPoly, nTransparencyPercent );
+ }
+ else
+ {
+ /// draw background opaque
+ if ( _rOut.GetFillColor() != aColor )
+ _rOut.SetFillColor( aColor );
+ _rOut.DrawRect( _rAlignedPaintRect.SVRect() );
+ }
+}
+
+/**
+ * This is a local help method to draw a background for a graphic
+ *
+ * Under certain circumstances we have to draw a background for a graphic.
+ * This method takes care of the conditions and draws the background with the
+ * corresponding color.
+ * Method introduced for bug fix #103876# in order to optimize drawing tiled
+ * background graphics. Previously, this code was integrated in method
+ * <lcl_DrawGraphic>.
+ * Method implemented as an inline, checking the conditions and calling method
+ * method <lcl_implDrawGraphicBackground(..)> for the intrinsic drawing.
+ *
+ * @param _rBackgrdBrush
+ * background brush contain the color the background has to be drawn.
+ *
+ * @param _rOut
+ * output device the background has to be drawn in.
+ *
+ * @param _rAlignedPaintRect
+ * paint rectangle in the output device, which has to be drawn with the background.
+ * rectangle have to be aligned by method ::SwAlignRect
+ *
+ * @param _rGraphicObj
+ * graphic object, for which the background has to be drawn. Used for checking
+ * the transparency of its bitmap, its type and if the graphic is drawn transparent
+ *
+ * @param _bNumberingGraphic
+ * boolean indicating that graphic is used as a numbering.
+ *
+ * @param _bBackgrdAlreadyDrawn
+ * boolean (optional; default: false) indicating, if the background is already drawn.
+*/
+static void lcl_DrawGraphicBackground( const SvxBrushItem& _rBackgrdBrush,
+ OutputDevice& _rOut,
+ const SwRect& _rAlignedPaintRect,
+ const GraphicObject& _rGraphicObj,
+ bool _bNumberingGraphic,
+ SwPaintProperties const & properties,
+ bool _bBackgrdAlreadyDrawn = false)
+{
+ // draw background with background color, if
+ // (1) graphic is not used as a numbering AND
+ // (2) background is not already drawn AND
+ // (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists
+ if ( !_bNumberingGraphic &&
+ !_bBackgrdAlreadyDrawn &&
+ ( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE )
+ )
+ {
+ lcl_implDrawGraphicBackground( _rBackgrdBrush, _rOut, _rAlignedPaintRect, _rGraphicObj, properties );
+ }
+}
+
+/**
+ * NNOTE: the transparency of the background graphic is saved in
+ * SvxBrushItem.GetGraphicObject(<shell>).GetAttr().Set/GetTransparency()
+ * and is considered in the drawing of the graphic
+ *
+ * Thus, to provide transparent background graphic for text frames nothing
+ * has to be coded
+ *
+ * Use align rectangle for drawing graphic Pixel-align coordinates for
+ * drawing graphic
+ * Outsource code for drawing background of the graphic
+ * with a background color in method <lcl_DrawGraphicBackground>
+ *
+ * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool>
+ */
+static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext &rOutDev,
+ const SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut,
+ bool bGrfNum,
+ SwPaintProperties const & properties,
+ bool bBackgrdAlreadyDrawn )
+ // add parameter <bBackgrdAlreadyDrawn> to indicate
+ // that the background is already drawn.
+{
+ // Calculate align rectangle from parameter <rGrf> and use aligned
+ // rectangle <aAlignedGrfRect> in the following code
+ SwRect aAlignedGrfRect = rGrf;
+ ::SwAlignRect( aAlignedGrfRect, &rSh, &rOutDev );
+
+ // Change type from <bool> to <bool>.
+ const bool bNotInside = !rOut.Contains( aAlignedGrfRect );
+ if ( bNotInside )
+ {
+ rOutDev.Push( vcl::PushFlags::CLIPREGION );
+ rOutDev.IntersectClipRegion( rOut.SVRect() );
+ }
+
+ GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject());
+
+ OUString aOriginURL = pGrf->GetGraphic().getOriginURL();
+ if (pGrf->GetGraphic().GetType() == GraphicType::Default && !aOriginURL.isEmpty())
+ {
+ Graphic aGraphic = vcl::graphic::loadFromURL(aOriginURL);
+ pGrf->SetGraphic(aGraphic);
+ }
+
+ // Outsource drawing of background with a background color
+ ::lcl_DrawGraphicBackground( rBrush, rOutDev, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn );
+
+ // Because for drawing a graphic left-top-corner and size coordinates are
+ // used, these coordinates have to be determined on pixel level.
+ ::SwAlignGrfRect( &aAlignedGrfRect, rOutDev );
+
+ const basegfx::B2DHomMatrix aGraphicTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aAlignedGrfRect.Width(), aAlignedGrfRect.Height(),
+ aAlignedGrfRect.Left(), aAlignedGrfRect.Top()));
+
+ paintGraphicUsingPrimitivesHelper(
+ rOutDev,
+ *pGrf,
+ pGrf->GetAttr(),
+ aGraphicTransform,
+ OUString(),
+ OUString(),
+ OUString());
+
+ if ( bNotInside )
+ rOutDev.Pop();
+}
+
+bool DrawFillAttributes(
+ const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
+ const SwRect& rOriginalLayoutRect,
+ const SwRegionRects& rPaintRegion,
+ const basegfx::utils::B2DClipState& rClipState,
+ vcl::RenderContext& rOut)
+{
+ if(rFillAttributes && rFillAttributes->isUsed())
+ {
+ basegfx::B2DRange aPaintRange(
+ rPaintRegion.GetOrigin().Left(),
+ rPaintRegion.GetOrigin().Top(),
+ rPaintRegion.GetOrigin().Right(),
+ rPaintRegion.GetOrigin().Bottom());
+
+ if (!aPaintRange.isEmpty() &&
+ !rPaintRegion.empty() &&
+ !basegfx::fTools::equalZero(aPaintRange.getWidth()) &&
+ !basegfx::fTools::equalZero(aPaintRange.getHeight()))
+ {
+ // need to expand for correct AAed and non-AAed visualization as primitive.
+ // This must probably be removed again when we will be able to get all Writer visualization
+ // as primitives and Writer prepares all it's stuff in high precision coordinates (also
+ // needs to avoid moving boundaries around to better show overlapping stuff...)
+ if(SvtOptionsDrawinglayer::IsAntiAliasing())
+ {
+ // if AAed in principle expand by 0.5 in all directions. Since painting edges of
+ // AAed regions does not add to no transparence (0.5 opacity covered by 0.5 opacity
+ // is not full opacity but 0.75 opacity) we need some overlap here to avoid paint
+ // artifacts. Checked experimentally - a little bit more in Y is needed, probably
+ // due to still existing integer alignment and crunching in writer.
+ static const double fExpandX = 0.55;
+ static const double fExpandY = 0.70;
+ const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(fExpandX, fExpandY));
+
+ aPaintRange.expand(aPaintRange.getMinimum() - aSingleUnit);
+ aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
+ }
+ else
+ {
+ // if not AAed expand by one unit to bottom right due to the missing unit
+ // from SwRect/Rectangle integer handling
+ const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0));
+
+ aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
+ }
+
+ const basegfx::B2DRange aDefineRange(
+ rOriginalLayoutRect.Left(),
+ rOriginalLayoutRect.Top(),
+ rOriginalLayoutRect.Right(),
+ rOriginalLayoutRect.Bottom());
+
+ const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rFillAttributes->getPrimitive2DSequence(
+ aPaintRange,
+ aDefineRange);
+
+ if(rSequence.size())
+ {
+ drawinglayer::primitive2d::Primitive2DContainer const*
+ pPrimitives(&rSequence);
+ drawinglayer::primitive2d::Primitive2DContainer primitives;
+ // tdf#86578 the awful lcl_SubtractFlys hack
+ if (rPaintRegion.size() > 1 || rPaintRegion[0] != rPaintRegion.GetOrigin())
+ {
+ basegfx::B2DPolyPolygon const& maskRegion(rClipState.getClipPoly());
+ primitives.resize(1);
+ primitives[0] = new drawinglayer::primitive2d::MaskPrimitive2D(
+ maskRegion, drawinglayer::primitive2d::Primitive2DContainer(rSequence));
+ pPrimitives = &primitives;
+ }
+ assert(pPrimitives && pPrimitives->size());
+
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D(
+ basegfx::B2DHomMatrix(),
+ rOut.GetViewTransformation(),
+ aPaintRange,
+ nullptr,
+ 0.0);
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
+ rOut,
+ aViewInformation2D) );
+ pProcessor->process(*pPrimitives);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void DrawGraphic(
+ const SvxBrushItem *pBrush,
+ vcl::RenderContext &rOutDev,
+ const SwRect &rOrg,
+ const SwRect &rOut,
+ const sal_uInt8 nGrfNum,
+ const bool bConsiderBackgroundTransparency )
+ // Add 6th parameter to indicate that method should
+ // consider background transparency, saved in the color of the brush item
+{
+ SwViewShell &rSh = *gProp.pSGlobalShell;
+ bool bReplaceGrfNum = GRFNUM_REPLACE == nGrfNum;
+ bool bGrfNum = GRFNUM_NO != nGrfNum;
+ Size aGrfSize;
+ SvxGraphicPosition ePos = GPOS_NONE;
+ if( pBrush && !bReplaceGrfNum )
+ {
+ if( rSh.GetViewOptions()->IsGraphic() )
+ {
+ OUString referer;
+ SfxObjectShell * sh = rSh.GetDoc()->GetPersist();
+ if (sh != nullptr && sh->HasName()) {
+ referer = sh->GetMedium()->GetName();
+ }
+ const Graphic* pGrf = pBrush->GetGraphic(referer);
+ if( pGrf && GraphicType::NONE != pGrf->GetType() )
+ {
+ ePos = pBrush->GetGraphicPos();
+ if( pGrf->IsSupportedGraphic() )
+ // don't the use the specific output device! Bug 94802
+ aGrfSize = ::GetGraphicSizeTwip( *pGrf, nullptr );
+ }
+ }
+ else
+ bReplaceGrfNum = bGrfNum;
+ }
+
+ SwRect aGrf;
+ aGrf.SSize( aGrfSize );
+ bool bDraw = true;
+ bool bRetouche = true;
+ switch ( ePos )
+ {
+ case GPOS_LT:
+ aGrf.Pos() = rOrg.Pos();
+ break;
+
+ case GPOS_MT:
+ aGrf.Pos().setY( rOrg.Top() );
+ aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
+ break;
+
+ case GPOS_RT:
+ aGrf.Pos().setY( rOrg.Top() );
+ aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_LM:
+ aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
+ aGrf.Pos().setX( rOrg.Left() );
+ break;
+
+ case GPOS_MM:
+ aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
+ aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
+ break;
+
+ case GPOS_RM:
+ aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
+ aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_LB:
+ aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
+ aGrf.Pos().setX( rOrg.Left() );
+ break;
+
+ case GPOS_MB:
+ aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
+ aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
+ break;
+
+ case GPOS_RB:
+ aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
+ aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_AREA:
+ aGrf = rOrg;
+ // Despite the fact that the background graphic has to fill the complete
+ // area, we already checked, whether the graphic will completely fill out
+ // the region the <rOut> that is to be painted. Thus, nothing has to be
+ // touched again.
+ // E.g. this is the case for a Fly Frame without a background
+ // brush positioned on the border of the page which inherited the background
+ // brush from the page.
+ bRetouche = !rOut.Contains( aGrf );
+ break;
+
+ case GPOS_TILED:
+ {
+ // draw background of tiled graphic before drawing tiled graphic in loop
+ // determine graphic object
+ GraphicObject* pGraphicObj = const_cast< GraphicObject* >(pBrush->GetGraphicObject());
+ // calculate aligned paint rectangle
+ SwRect aAlignedPaintRect = rOut;
+ ::SwAlignRect( aAlignedPaintRect, &rSh, &rOutDev );
+ // draw background color for aligned paint rectangle
+ lcl_DrawGraphicBackground( *pBrush, rOutDev, aAlignedPaintRect, *pGraphicObj, bGrfNum, gProp );
+
+ // set left-top-corner of background graphic to left-top-corner of the
+ // area, from which the background brush is determined.
+ aGrf.Pos() = rOrg.Pos();
+ // setup clipping at output device
+ rOutDev.Push( vcl::PushFlags::CLIPREGION );
+ rOutDev.IntersectClipRegion( rOut.SVRect() );
+ // use new method <GraphicObject::DrawTiled(::)>
+ {
+ // calculate paint offset
+ Point aPaintOffset( aAlignedPaintRect.Pos() - aGrf.Pos() );
+ // draw background graphic tiled for aligned paint rectangle
+ // #i42643#
+ // For PDF export, every draw operation for bitmaps takes a
+ // noticeable amount of place (~50 characters). Thus, optimize
+ // between tile bitmap size and number of drawing operations here.
+
+ // A_out
+ // n_chars = k1 * ---------- + k2 * A_bitmap
+ // A_bitmap
+
+ // minimum n_chars is obtained for (derive for A_bitmap,
+ // set to 0, take positive solution):
+ // k1
+ // A_bitmap = Sqrt( ---- A_out )
+ // k2
+
+ // where k1 is the number of chars per draw operation, and
+ // k2 is the number of chars per bitmap pixel.
+ // This is approximately 50 and 7 for current PDF writer, respectively.
+
+ const double k1( 50 );
+ const double k2( 7 );
+ const Size aSize( aAlignedPaintRect.SSize() );
+ const double Abitmap( k1/k2 * static_cast<double>(aSize.Width())*aSize.Height() );
+
+ pGraphicObj->DrawTiled( rOutDev,
+ aAlignedPaintRect.SVRect(),
+ aGrf.SSize(),
+ Size( aPaintOffset.X(), aPaintOffset.Y() ),
+ std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
+ }
+ // reset clipping at output device
+ rOutDev.Pop();
+ // set <bDraw> and <bRetouche> to false, indicating that background
+ // graphic and background are already drawn.
+ bDraw = bRetouche = false;
+ }
+ break;
+
+ case GPOS_NONE:
+ bDraw = false;
+ break;
+
+ default: OSL_ENSURE( false, "new Graphic position?" );
+ }
+
+ /// init variable <bGrfBackgrdAlreadDrawn> to indicate, if background of
+ /// graphic is already drawn or not.
+ bool bGrfBackgrdAlreadyDrawn = false;
+ if ( bRetouche )
+ {
+ rOutDev.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ rOutDev.SetLineColor();
+
+ // check, if an existing background graphic (not filling the complete
+ // background) is transparent drawn and the background color is
+ // "no fill" respectively "auto fill", if background transparency
+ // has to be considered.
+ // If YES, memorize transparency of background graphic.
+ // check also, if background graphic bitmap is transparent.
+ bool bTransparentGrfWithNoFillBackgrd = false;
+ sal_Int32 nGrfTransparency = 0;
+ bool bGrfIsTransparent = false;
+ if ( (ePos != GPOS_NONE) &&
+ (ePos != GPOS_TILED) && (ePos != GPOS_AREA)
+ )
+ {
+ GraphicObject *pGrf = const_cast<GraphicObject*>(pBrush->GetGraphicObject());
+ if ( bConsiderBackgroundTransparency )
+ {
+ GraphicAttr aGrfAttr = pGrf->GetAttr();
+ if ( (aGrfAttr.IsTransparent()) &&
+ (pBrush->GetColor() == COL_TRANSPARENT)
+ )
+ {
+ bTransparentGrfWithNoFillBackgrd = true;
+ nGrfTransparency = 255 - aGrfAttr.GetAlpha();
+ }
+ }
+ if ( pGrf->IsTransparent() )
+ {
+ bGrfIsTransparent = true;
+ }
+ }
+
+ // to get color of brush, check background color against COL_TRANSPARENT ("no fill"/"auto fill")
+ // instead of checking, if transparency is not set.
+ const Color aColor( pBrush &&
+ ( (pBrush->GetColor() != COL_TRANSPARENT) ||
+ gProp.bSFlyMetafile )
+ ? pBrush->GetColor()
+ : aGlobalRetoucheColor );
+
+ // determine, if background region have to be
+ // drawn transparent.
+ // background region has to be drawn transparent, if
+ // background transparency have to be considered
+ // AND
+ // ( background color is transparent OR
+ // background graphic is transparent and background color is "no fill"
+ // )
+
+ enum DrawStyle {
+ Default,
+ Transparent,
+ } eDrawStyle = Default;
+
+ if (bConsiderBackgroundTransparency &&
+ ( ( aColor.IsTransparent()) ||
+ bTransparentGrfWithNoFillBackgrd ) )
+ {
+ eDrawStyle = Transparent;
+ }
+
+ // #i75614# reset draw mode in high contrast mode in order to get fill color set
+ const DrawModeFlags nOldDrawMode = rOutDev.GetDrawMode();
+ if ( gProp.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ rOutDev.SetDrawMode( DrawModeFlags::Default );
+ }
+
+ // If background region has to be drawn transparent, set only the RGB values of the background color as
+ // the fill color for the output device.
+ switch (eDrawStyle)
+ {
+ case Transparent:
+ {
+ if( rOutDev.GetFillColor() != aColor.GetRGBColor() )
+ rOutDev.SetFillColor( aColor.GetRGBColor() );
+ break;
+ }
+ default:
+ {
+ if( rOutDev.GetFillColor() != aColor )
+ rOutDev.SetFillColor( aColor );
+ break;
+ }
+ }
+
+ // #i75614#
+ // restore draw mode
+ rOutDev.SetDrawMode( nOldDrawMode );
+
+ switch (eDrawStyle)
+ {
+ case Transparent:
+ {
+ // background region have to be drawn transparent.
+ // Thus, create a poly-polygon from the region and draw it with
+ // the corresponding transparency percent.
+ tools::PolyPolygon aDrawPoly( rOut.SVRect() );
+ if ( aGrf.HasArea() )
+ {
+ if ( !bGrfIsTransparent )
+ {
+ // subtract area of background graphic from draw area
+ // Consider only that part of the graphic area that is overlapping with draw area.
+ SwRect aTmpGrf = aGrf;
+ aTmpGrf.Intersection( rOut );
+ if ( aTmpGrf.HasArea() )
+ {
+ tools::Polygon aGrfPoly( aTmpGrf.SVRect() );
+ aDrawPoly.Insert( aGrfPoly );
+ }
+ }
+ else
+ bGrfBackgrdAlreadyDrawn = true;
+ }
+ // calculate transparency percent:
+ // ( <transparency value[0x01..0xFF]>*100 + 0x7F ) / 0xFF
+ // If there is a background graphic with a background color "no fill"/"auto fill",
+ // the transparency value is taken from the background graphic,
+ // otherwise take the transparency value from the color.
+ sal_Int8 nTransparencyPercent = static_cast<sal_Int8>(
+ (( bTransparentGrfWithNoFillBackgrd ? nGrfTransparency : (255 - aColor.GetAlpha())
+ )*100 + 0x7F)/0xFF);
+ // draw poly-polygon transparent
+ rOutDev.DrawTransparent( aDrawPoly, nTransparencyPercent );
+
+ break;
+ }
+ case Default:
+ default:
+ {
+ SwRegionRects aRegion( rOut, 4 );
+ if ( !bGrfIsTransparent )
+ aRegion -= aGrf;
+ else
+ bGrfBackgrdAlreadyDrawn = true;
+ // loop rectangles of background region, which has to be drawn
+ for( size_t i = 0; i < aRegion.size(); ++i )
+ {
+ rOutDev.DrawRect( aRegion[i].SVRect() );
+ }
+ }
+ }
+ rOutDev.Pop();
+ }
+
+ if( bDraw && aGrf.Overlaps( rOut ) )
+ lcl_DrawGraphic( *pBrush, rOutDev, rSh, aGrf, rOut, bGrfNum, gProp,
+ bGrfBackgrdAlreadyDrawn );
+
+ if( bReplaceGrfNum )
+ {
+ const BitmapEx& rBmp = rSh.GetReplacementBitmap(false);
+ vcl::Font aTmp( rOutDev.GetFont() );
+ Graphic::DrawEx(rOutDev, OUString(), aTmp, rBmp, rOrg.Pos(), rOrg.SSize());
+ }
+}
+
+/**
+ * Local helper for SwRootFrame::PaintSwFrame(..) - Adjust given rectangle to pixel size
+ *
+ * By OD at 27.09.2002 for #103636#
+ * In order to avoid paint errors caused by multiple alignments (e.g. ::SwAlignRect(..))
+ * and other changes to the to be painted rectangle, this method is called for the
+ * rectangle to be painted in order to adjust it to the pixel it is overlapping
+*/
+static void lcl_AdjustRectToPixelSize( SwRect& io_aSwRect, const vcl::RenderContext &aOut )
+{
+ // local constant object of class <Size> to determine number of Twips
+ // representing a pixel.
+ const Size aTwipToPxSize( aOut.PixelToLogic( Size( 1,1 )) );
+
+ // local object of class <Rectangle> in Twip coordinates
+ // calculated from given rectangle aligned to pixel centers.
+ const tools::Rectangle aPxCenterRect = aOut.PixelToLogic(
+ aOut.LogicToPixel( io_aSwRect.SVRect() ) );
+
+ // local constant object of class <Rectangle> representing given rectangle
+ // in pixel.
+ const tools::Rectangle aOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
+
+ // calculate adjusted rectangle from pixel centered rectangle.
+ // Due to rounding differences <aPxCenterRect> doesn't exactly represents
+ // the Twip-centers. Thus, adjust borders by half of pixel width/height plus 1.
+ // Afterwards, adjust calculated Twip-positions of the all borders.
+ tools::Rectangle aSizedRect = aPxCenterRect;
+ aSizedRect.AdjustLeft( -(aTwipToPxSize.Width()/2 + 1) );
+ aSizedRect.AdjustRight( aTwipToPxSize.Width()/2 + 1 );
+ aSizedRect.AdjustTop( -(aTwipToPxSize.Height()/2 + 1) );
+ aSizedRect.AdjustBottom(aTwipToPxSize.Height()/2 + 1);
+
+ // adjust left()
+ while ( aOut.LogicToPixel(aSizedRect).Left() < aOrgPxRect.Left() )
+ {
+ aSizedRect.AdjustLeft( 1 );
+ }
+ // adjust right()
+ while ( aOut.LogicToPixel(aSizedRect).Right() > aOrgPxRect.Right() )
+ {
+ aSizedRect.AdjustRight( -1 );
+ }
+ // adjust top()
+ while ( aOut.LogicToPixel(aSizedRect).Top() < aOrgPxRect.Top() )
+ {
+ aSizedRect.AdjustTop( 1 );
+ }
+ // adjust bottom()
+ while ( aOut.LogicToPixel(aSizedRect).Bottom() > aOrgPxRect.Bottom() )
+ {
+ aSizedRect.AdjustBottom( -1 );
+ }
+
+ io_aSwRect = SwRect( aSizedRect );
+
+#if OSL_DEBUG_LEVEL > 0
+ tools::Rectangle aTestOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
+ tools::Rectangle aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
+ OSL_ENSURE( aTestOrgPxRect == aTestNewPxRect,
+ "Error in lcl_AlignRectToPixelSize(..): Adjusted rectangle has incorrect position or size");
+ // check Left()
+ aSizedRect.AdjustLeft( -1 );
+ aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
+ OSL_ENSURE( aTestOrgPxRect.Left() >= (aTestNewPxRect.Left()+1),
+ "Error in lcl_AlignRectToPixelSize(..): Left() not correct adjusted");
+ aSizedRect.AdjustLeft( 1 );
+ // check Right()
+ aSizedRect.AdjustRight( 1 );
+ aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
+ OSL_ENSURE( aTestOrgPxRect.Right() <= (aTestNewPxRect.Right()-1),
+ "Error in lcl_AlignRectToPixelSize(..): Right() not correct adjusted");
+ aSizedRect.AdjustRight( -1 );
+ // check Top()
+ aSizedRect.AdjustTop( -1 );
+ aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
+ OSL_ENSURE( aTestOrgPxRect.Top() >= (aTestNewPxRect.Top()+1),
+ "Error in lcl_AlignRectToPixelSize(..): Top() not correct adjusted");
+ aSizedRect.AdjustTop( 1 );
+ // check Bottom()
+ aSizedRect.AdjustBottom( 1 );
+ aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
+ OSL_ENSURE( aTestOrgPxRect.Bottom() <= (aTestNewPxRect.Bottom()-1),
+ "Error in lcl_AlignRectToPixelSize(..): Bottom() not correct adjusted");
+ aSizedRect.AdjustBottom( -1 );
+#endif
+}
+
+// FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES START
+
+namespace {
+
+struct SwLineEntry
+{
+ SwTwips mnKey;
+ SwTwips mnStartPos;
+ SwTwips mnEndPos;
+ SwTwips mnLimitedEndPos;
+ bool mbOuter;
+
+ svx::frame::Style maAttribute;
+
+ enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 };
+
+ enum class VerticalType { LEFT, RIGHT };
+
+public:
+ SwLineEntry( SwTwips nKey,
+ SwTwips nStartPos,
+ SwTwips nEndPos,
+ bool bOuter,
+ const svx::frame::Style& rAttribute );
+
+ OverlapType Overlaps( const SwLineEntry& rComp ) const;
+
+ /**
+ * Assuming that this entry is for a Word-style covering cell and the border matching eType is
+ * set, limit the end position of this border in case covered cells have no borders set.
+ */
+ void LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType);
+};
+
+}
+
+SwLineEntry::SwLineEntry( SwTwips nKey,
+ SwTwips nStartPos,
+ SwTwips nEndPos,
+ bool bOuter,
+ const svx::frame::Style& rAttribute )
+ : mnKey( nKey ),
+ mnStartPos( nStartPos ),
+ mnEndPos( nEndPos ),
+ mnLimitedEndPos(0),
+ mbOuter(bOuter),
+ maAttribute( rAttribute )
+{
+}
+
+/*
+
+ 1. ---------- rOld
+ ---------- rNew
+
+ 2. ---------- rOld
+ ------------- rNew
+
+ 3. ------- rOld
+ ------------- rNew
+
+ 4. ------------- rOld
+ ---------- rNew
+
+ 5. ---------- rOld
+ ---- rNew
+
+ 6. ---------- rOld
+ ---------- rNew
+
+ 7. ------------- rOld
+ ---------- rNew
+
+ 8. ---------- rOld
+ ------------- rNew
+
+ 9. ---------- rOld
+ ---------- rNew
+*/
+
+SwLineEntry::OverlapType SwLineEntry::Overlaps( const SwLineEntry& rNew ) const
+{
+ SwLineEntry::OverlapType eRet = OVERLAP3;
+
+ if ( mnStartPos >= rNew.mnEndPos || mnEndPos <= rNew.mnStartPos )
+ eRet = NO_OVERLAP;
+
+ // 1, 2, 3
+ else if ( mnEndPos < rNew.mnEndPos )
+ eRet = OVERLAP1;
+
+ // 4, 5, 6, 7
+ else if (mnStartPos <= rNew.mnStartPos)
+ eRet = OVERLAP2;
+
+ // 8, 9
+ return eRet;
+}
+
+void SwLineEntry::LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType)
+{
+ if (!rFrame.IsCellFrame())
+ {
+ return;
+ }
+
+ const auto& rCellFrame = static_cast<const SwCellFrame&>(rFrame);
+ std::vector<const SwCellFrame*> aCoveredCells = rCellFrame.GetCoveredCells();
+ // Iterate in reverse order, so we can stop at the first cell that has a border. This can
+ // determine what is the minimal end position that is safe to use as a limit.
+ for (auto it = aCoveredCells.rbegin(); it != aCoveredCells.rend(); ++it)
+ {
+ const SwCellFrame* pCoveredCell = *it;
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCoveredCell );
+ const SwBorderAttrs& rAttrs = *aAccess.Get();
+ const SvxBoxItem& rBox = rAttrs.GetBox();
+ if (eType == VerticalType::LEFT && rBox.GetLeft())
+ {
+ break;
+ }
+
+ if (eType == VerticalType::RIGHT && rBox.GetRight())
+ {
+ break;
+ }
+
+ mnLimitedEndPos = pCoveredCell->getFrameArea().Top();
+ }
+}
+
+namespace {
+
+struct lt_SwLineEntry
+{
+ bool operator()( const SwLineEntry& e1, const SwLineEntry& e2 ) const
+ {
+ return e1.mnStartPos < e2.mnStartPos;
+ }
+};
+
+}
+
+typedef std::set< SwLineEntry, lt_SwLineEntry > SwLineEntrySet;
+typedef std::map< SwTwips, SwLineEntrySet > SwLineEntryMap;
+
+namespace {
+
+class SwTabFramePainter
+{
+ SwLineEntryMap maVertLines;
+ SwLineEntryMap maHoriLines;
+ const SwTabFrame& mrTabFrame;
+
+ void Insert( SwLineEntry&, bool bHori );
+ void Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect &rPaintArea);
+ void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
+ void FindStylesForLine( Point&,
+ Point&,
+ svx::frame::Style*,
+ bool bHori,
+ bool bOuter ) const;
+
+public:
+ explicit SwTabFramePainter( const SwTabFrame& rTabFrame );
+
+ void PaintLines( OutputDevice& rDev, const SwRect& rRect ) const;
+};
+
+}
+
+SwTabFramePainter::SwTabFramePainter( const SwTabFrame& rTabFrame )
+ : mrTabFrame( rTabFrame )
+{
+ SwRect aPaintArea = rTabFrame.GetUpper()->GetPaintArea();
+ HandleFrame(rTabFrame, aPaintArea);
+}
+
+void SwTabFramePainter::HandleFrame(const SwLayoutFrame& rLayoutFrame, const SwRect& rPaintArea)
+{
+ // Add border lines of cell frames. Skip covered cells. Skip cells
+ // in special row span row, which do not have a negative row span:
+ if ( rLayoutFrame.IsCellFrame() && !rLayoutFrame.IsCoveredCell() )
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rLayoutFrame);
+ const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pThisCell->GetUpper());
+ const tools::Long nRowSpan = pThisCell->GetTabBox()->getRowSpan();
+ if ( !pRowFrame->IsRowSpanLine() || nRowSpan > 1 || nRowSpan < -1 )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), &rLayoutFrame );
+ const SwBorderAttrs& rAttrs = *aAccess.Get();
+ const SvxBoxItem& rBox = rAttrs.GetBox();
+ Insert(rLayoutFrame, rBox, rPaintArea);
+ }
+ }
+
+ // Recurse into lower layout frames, but do not recurse into lower tabframes.
+ const SwFrame* pLower = rLayoutFrame.Lower();
+ while ( pLower )
+ {
+ if (pLower->IsLayoutFrame() && !pLower->IsTabFrame())
+ {
+ const SwLayoutFrame* pLowerLayFrame = static_cast<const SwLayoutFrame*>(pLower);
+ HandleFrame(*pLowerLayFrame, rPaintArea);
+ }
+ pLower = pLower->GetNext();
+ }
+}
+
+void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) const
+{
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, rDev );
+
+ SwLineEntryMap::const_iterator aIter = maHoriLines.begin();
+ bool bHori = true;
+
+ // color for subsidiary lines:
+ const Color& rCol( SwViewOption::GetTableBoundariesColor() );
+
+ // high contrast mode:
+ // overrides the color of non-subsidiary lines.
+ const Color* pHCColor = nullptr;
+ DrawModeFlags nOldDrawMode = rDev.GetDrawMode();
+ if( gProp.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ pHCColor = &SwViewOption::GetFontColor();
+ rDev.SetDrawMode( DrawModeFlags::Default );
+ }
+
+ const SwFrame* pUpper = mrTabFrame.GetUpper();
+ SwRect aUpper( pUpper->getFramePrintArea() );
+ aUpper.Pos() += pUpper->getFrameArea().Pos();
+ SwRect aUpperAligned( aUpper );
+ ::SwAlignRect( aUpperAligned, gProp.pSGlobalShell, &rDev );
+
+ // prepare SdrFrameBorderDataVector
+ std::shared_ptr<drawinglayer::primitive2d::SdrFrameBorderDataVector> aData(
+ std::make_shared<drawinglayer::primitive2d::SdrFrameBorderDataVector>());
+
+ while ( true )
+ {
+ if ( bHori && aIter == maHoriLines.end() )
+ {
+ aIter = maVertLines.begin();
+ bHori = false;
+ }
+
+ if ( !bHori && aIter == maVertLines.end() )
+ break;
+
+ const SwLineEntrySet& rEntrySet = (*aIter).second;
+ for (const SwLineEntry& rEntry : rEntrySet)
+ {
+ const svx::frame::Style& rEntryStyle( rEntry.maAttribute );
+
+ Point aStart, aEnd;
+ if ( bHori )
+ {
+ aStart.setX( rEntry.mnStartPos );
+ aStart.setY( rEntry.mnKey );
+ aEnd.setX( rEntry.mnEndPos );
+ aEnd.setY( rEntry.mnKey );
+ }
+ else
+ {
+ aStart.setX( rEntry.mnKey );
+ aStart.setY( rEntry.mnStartPos );
+ aEnd.setX( rEntry.mnKey );
+ aEnd.setY( rEntry.mnEndPos );
+ }
+
+ svx::frame::Style aStyles[ 7 ];
+ aStyles[ 0 ] = rEntryStyle;
+ FindStylesForLine(aStart, aEnd, aStyles, bHori, rEntry.mbOuter);
+
+ if (!bHori && rEntry.mnLimitedEndPos)
+ {
+ aEnd.setY(rEntry.mnLimitedEndPos);
+ }
+
+ SwRect aRepaintRect( aStart, aEnd );
+
+ // the repaint rectangle has to be moved a bit for the centered lines:
+ SwTwips nRepaintRectSize = !rEntryStyle.GetWidth() ? 1 : rEntryStyle.GetWidth();
+ if ( bHori )
+ {
+ aRepaintRect.Height( 2 * nRepaintRectSize );
+ aRepaintRect.Pos().AdjustY( -nRepaintRectSize );
+
+ // To decide on visibility it is also necessary to expand the RepaintRect
+ // to left/right according existing BorderLine overlap matchings, else there
+ // will be repaint errors when scrolling in e.t TripleLine BorderLines.
+ // aStyles[1] == aLFromT, aStyles[3] == aLFromB, aStyles[4] == aRFromT, aStyles[6] == aRFromB
+ if(aStyles[1].IsUsed() || aStyles[3].IsUsed() || aStyles[4].IsUsed() || aStyles[6].IsUsed())
+ {
+ const double fLineWidthMaxLeft(std::max(aStyles[1].GetWidth(), aStyles[3].GetWidth()));
+ const double fLineWidthMaxRight(std::max(aStyles[4].GetWidth(), aStyles[6].GetWidth()));
+ aRepaintRect.Width(aRepaintRect.Width() + (fLineWidthMaxLeft + fLineWidthMaxRight));
+ aRepaintRect.Pos().AdjustX( -fLineWidthMaxLeft );
+ }
+ }
+ else
+ {
+ aRepaintRect.Width( 2 * nRepaintRectSize );
+ aRepaintRect.Pos().AdjustX( -nRepaintRectSize );
+
+ // Accordingly to horizontal case, but for top/bottom
+ // aStyles[3] == aTFromR, aStyles[1] == aTFromL, aStyles[6] == aBFromR, aStyles[4] == aBFromL
+ if(aStyles[3].IsUsed() || aStyles[1].IsUsed() || aStyles[6].IsUsed() || aStyles[4].IsUsed())
+ {
+ const double fLineWidthMaxTop(std::max(aStyles[3].GetWidth(), aStyles[1].GetWidth()));
+ const double fLineWidthMaxBottom(std::max(aStyles[6].GetWidth(), aStyles[4].GetWidth()));
+ aRepaintRect.Height(aRepaintRect.Height() + (fLineWidthMaxTop + fLineWidthMaxBottom));
+ aRepaintRect.Pos().AdjustY( -fLineWidthMaxTop );
+ }
+ }
+
+ if (!rRect.Overlaps(aRepaintRect))
+ {
+ continue;
+ }
+
+ // subsidiary lines
+ const Color* pTmpColor = nullptr;
+ if (0 == aStyles[ 0 ].GetWidth())
+ {
+ if (isTableBoundariesEnabled() && gProp.pSGlobalShell->GetWin())
+ aStyles[ 0 ].Set( rCol, rCol, rCol, false, 1, 0, 0 );
+ else
+ aStyles[0].SetType(SvxBorderLineStyle::NONE);
+ }
+ else
+ pTmpColor = pHCColor;
+
+ // The (twip) positions will be adjusted to meet these requirements:
+ // 1. The y coordinates are located in the middle of the pixel grid
+ // 2. The x coordinated are located at the beginning of the pixel grid
+ // This is done, because the horizontal lines are painted "at
+ // beginning", whereas the vertical lines are painted "centered".
+ // By making the line sizes a multiple of one pixel size, we can
+ // assure that all lines having the same twip size have the same
+ // pixel size, independent of their position on the screen.
+ Point aPaintStart = rDev.PixelToLogic( rDev.LogicToPixel(aStart) );
+ Point aPaintEnd = rDev.PixelToLogic( rDev.LogicToPixel(aEnd) );
+
+ if (gProp.pSGlobalShell->GetWin())
+ {
+ // The table borders do not use SwAlignRect, but all the other frames do.
+ // Therefore we tweak the outer borders a bit to achieve that the outer
+ // borders match the subsidiary lines of the upper:
+ if (aStart.X() == aUpper.Left())
+ aPaintStart.setX( aUpperAligned.Left() );
+ else if (aStart.X() == aUpper.Right_())
+ aPaintStart.setX( aUpperAligned.Right_() );
+ if (aStart.Y() == aUpper.Top())
+ aPaintStart.setY( aUpperAligned.Top() );
+ else if (aStart.Y() == aUpper.Bottom_())
+ aPaintStart.setY( aUpperAligned.Bottom_() );
+
+ if (aEnd.X() == aUpper.Left())
+ aPaintEnd.setX( aUpperAligned.Left() );
+ else if (aEnd.X() == aUpper.Right_())
+ aPaintEnd.setX( aUpperAligned.Right_() );
+ if (aEnd.Y() == aUpper.Top())
+ aPaintEnd.setY( aUpperAligned.Top() );
+ else if (aEnd.Y() == aUpper.Bottom_())
+ aPaintEnd.setY( aUpperAligned.Bottom_() );
+ }
+
+ if(aStyles[0].IsUsed())
+ {
+ if (bHori)
+ {
+ const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
+ const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
+
+ if(!aX.equalZero())
+ {
+ const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
+ aData->emplace_back(
+ aOrigin,
+ aX,
+ aStyles[0],
+ pTmpColor);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ rInstance.addSdrConnectStyleData(true, aStyles[1], -aY, true); // aLFromT
+ rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aLFromL
+ rInstance.addSdrConnectStyleData(true, aStyles[3], aY, false); // aLFromB
+
+ rInstance.addSdrConnectStyleData(false, aStyles[4], -aY, true); // aRFromT
+ rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aRFromR
+ rInstance.addSdrConnectStyleData(false, aStyles[6], aY, false); // aRFromB
+ }
+ }
+ else // vertical
+ {
+ const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
+ const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
+
+ if(!aX.equalZero())
+ {
+ const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
+ aData->emplace_back(
+ aOrigin,
+ aX,
+ aStyles[0],
+ pTmpColor);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ rInstance.addSdrConnectStyleData(true, aStyles[3], -aY, false); // aTFromR
+ rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aTFromT
+ rInstance.addSdrConnectStyleData(true, aStyles[1], aY, true); // aTFromL
+
+ rInstance.addSdrConnectStyleData(false, aStyles[6], -aY, false); // aBFromR
+ rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aBFromB
+ rInstance.addSdrConnectStyleData(false, aStyles[4], aY, true); // aBFromL
+ }
+ }
+ }
+ }
+ ++aIter;
+ }
+
+ // create instance of SdrFrameBorderPrimitive2D if
+ // SdrFrameBorderDataVector is used
+ if(!aData->empty())
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aSequence;
+ aSequence.append(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
+ aData,
+ true))); // force visualization to minimal one discrete unit (pixel)
+ // paint
+ mrTabFrame.ProcessPrimitives(aSequence);
+ }
+
+ // restore output device:
+ rDev.SetDrawMode( nOldDrawMode );
+}
+
+/**
+ * Finds the lines that join the line defined by (StartPoint, EndPoint) in either
+ * StartPoint or Endpoint. The styles of these lines are required for DR's magic
+ * line painting functions
+ */
+void SwTabFramePainter::FindStylesForLine( Point& rStartPoint,
+ Point& rEndPoint,
+ svx::frame::Style* pStyles,
+ bool bHori, bool bOuter ) const
+{
+ // For example, aLFromB means: this vertical line intersects my horizontal line at its left end,
+ // from bottom.
+ // pStyles[ 1 ] = bHori ? aLFromT : TFromL
+ // pStyles[ 2 ] = bHori ? aLFromL : TFromT,
+ // pStyles[ 3 ] = bHori ? aLFromB : TFromR,
+ // pStyles[ 4 ] = bHori ? aRFromT : BFromL,
+ // pStyles[ 5 ] = bHori ? aRFromR : BFromB,
+ // pStyles[ 6 ] = bHori ? aRFromB : BFromR,
+
+ bool bWordTableCell = false;
+ SwViewShell* pShell = mrTabFrame.getRootFrame()->GetCurrShell();
+ if (pShell)
+ {
+ const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
+ bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
+ }
+
+ SwLineEntryMap::const_iterator aMapIter = maVertLines.find( rStartPoint.X() );
+ OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
+ const SwLineEntrySet& rVertSet = (*aMapIter).second;
+
+ for ( const SwLineEntry& rEntry : rVertSet )
+ {
+ if ( bHori )
+ {
+ if ( rStartPoint.Y() == rEntry.mnStartPos )
+ pStyles[ 3 ] = rEntry.maAttribute;
+ else if ( rStartPoint.Y() == rEntry.mnEndPos )
+ pStyles[ 1 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rStartPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rStartPoint.AdjustX(rEntry.maAttribute.GetWidth());
+ }
+ }
+ else
+ {
+ if ( rStartPoint.Y() == rEntry.mnEndPos )
+ pStyles[ 2 ] = rEntry.maAttribute;
+ else if ( rEndPoint.Y() == rEntry.mnStartPos )
+ pStyles[ 5 ] = rEntry.maAttribute;
+ }
+ }
+
+ aMapIter = maHoriLines.find( rStartPoint.Y() );
+ OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
+ const SwLineEntrySet& rHoriSet = (*aMapIter).second;
+
+ for ( const SwLineEntry& rEntry : rHoriSet )
+ {
+ if ( bHori )
+ {
+ if ( rStartPoint.X() == rEntry.mnEndPos )
+ pStyles[ 2 ] = rEntry.maAttribute;
+ else if ( rEndPoint.X() == rEntry.mnStartPos )
+ pStyles[ 5 ] = rEntry.maAttribute;
+ }
+ else
+ {
+ if ( rStartPoint.X() == rEntry.mnEndPos )
+ pStyles[ 1 ] = rEntry.maAttribute;
+ else if ( rStartPoint.X() == rEntry.mnStartPos )
+ pStyles[ 3 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rStartPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rStartPoint.AdjustY(rEntry.maAttribute.GetWidth());
+ }
+ }
+ }
+
+ if ( bHori )
+ {
+ aMapIter = maVertLines.find( rEndPoint.X() );
+ OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
+ const SwLineEntrySet& rVertSet2 = (*aMapIter).second;
+
+ for ( const SwLineEntry& rEntry : rVertSet2 )
+ {
+ if ( rEndPoint.Y() == rEntry.mnStartPos )
+ pStyles[ 6 ] = rEntry.maAttribute;
+ else if ( rEndPoint.Y() == rEntry.mnEndPos )
+ pStyles[ 4 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rEndPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rEndPoint.AdjustX(-rEntry.maAttribute.GetWidth());
+ }
+ }
+ }
+ else
+ {
+ aMapIter = maHoriLines.find( rEndPoint.Y() );
+ OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
+ const SwLineEntrySet& rHoriSet2 = (*aMapIter).second;
+
+ for ( const SwLineEntry& rEntry : rHoriSet2 )
+ {
+ if ( rEndPoint.X() == rEntry.mnEndPos )
+ pStyles[ 4 ] = rEntry.maAttribute;
+ else if ( rEndPoint.X() == rEntry.mnStartPos )
+ pStyles[ 6 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rEndPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rEndPoint.AdjustY(-rEntry.maAttribute.GetWidth());
+ }
+ }
+ }
+}
+
+/**
+ * Special case: #i9860#
+ * first line in follow table without repeated headlines
+ */
+static bool lcl_IsFirstRowInFollowTableWithoutRepeatedHeadlines(
+ SwTabFrame const& rTabFrame, SwFrame const& rFrame, SvxBoxItem const& rBoxItem)
+{
+ SwRowFrame const*const pThisRowFrame =
+ dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
+ return (pThisRowFrame
+ && (pThisRowFrame->GetUpper() == &rTabFrame)
+ && rTabFrame.IsFollow()
+ && !rTabFrame.GetTable()->GetRowsToRepeat()
+ && ( !pThisRowFrame->GetPrev()
+ || static_cast<const SwRowFrame*>(pThisRowFrame->GetPrev())
+ ->IsRowSpanLine())
+ && !rBoxItem.GetTop()
+ && rBoxItem.GetBottom());
+}
+
+void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect& rPaintArea)
+{
+ // build 4 line entries for the 4 borders:
+ SwRect aBorderRect = rFrame.getFrameArea();
+
+ aBorderRect.Intersection(rPaintArea);
+
+ bool const bBottomAsTop(lcl_IsFirstRowInFollowTableWithoutRepeatedHeadlines(
+ mrTabFrame, rFrame, rBoxItem));
+ bool const bVert = mrTabFrame.IsVertical();
+ bool const bR2L = mrTabFrame.IsRightToLeft();
+
+ bool bWordTableCell = false;
+ SwViewShell* pShell = rFrame.getRootFrame()->GetCurrShell();
+ if (pShell)
+ {
+ const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
+ bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
+ }
+
+ // no scaling needed, it's all in the primitives and the target device
+ svx::frame::Style aL(rBoxItem.GetLeft(), 1.0);
+ aL.SetWordTableCell(bWordTableCell);
+ svx::frame::Style aR(rBoxItem.GetRight(), 1.0);
+ aR.SetWordTableCell(bWordTableCell);
+ svx::frame::Style aT(rBoxItem.GetTop(), 1.0);
+ aT.SetWordTableCell(bWordTableCell);
+ svx::frame::Style aB(rBoxItem.GetBottom(), 1.0);
+ aB.SetWordTableCell(bWordTableCell);
+
+ // First cell in a row.
+ bool bLeftIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetLower() == &rFrame;
+ // Last cell in a row.
+ bool bRightIsOuter = rFrame.IsCellFrame() && rFrame.GetNext() == nullptr;
+ // First row in a table.
+ bool bTopIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetUpper()->GetLower() == rFrame.GetUpper();
+ // Last row in a table.
+ bool bBottomIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetNext() == nullptr;
+
+ aR.MirrorSelf();
+ if (!bWordTableCell || !bBottomIsOuter)
+ {
+ // Outer horizontal lines are never mirrored in Word.
+ aB.MirrorSelf();
+ }
+
+ const SwTwips nLeft = aBorderRect.Left_();
+ const SwTwips nRight = aBorderRect.Right_();
+ const SwTwips nTop = aBorderRect.Top_();
+ const SwTwips nBottom = aBorderRect.Bottom_();
+
+ aL.SetRefMode( svx::frame::RefMode::Centered );
+ aR.SetRefMode( svx::frame::RefMode::Centered );
+ aT.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
+ aB.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
+
+ if (bWordTableCell && bLeftIsOuter)
+ {
+ // Outer vertical lines are always mirrored in Word.
+ aL.MirrorSelf();
+ }
+
+ SwLineEntry aLeft (nLeft, nTop, nBottom, bLeftIsOuter,
+ bVert ? aB : (bR2L ? aR : aL));
+ if (bWordTableCell && rBoxItem.GetLeft())
+ {
+ aLeft.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::LEFT);
+ }
+
+ SwLineEntry aRight (nRight, nTop, nBottom, bRightIsOuter,
+ bVert ? (bBottomAsTop ? aB : aT) : (bR2L ? aL : aR));
+ if (bWordTableCell && rBoxItem.GetRight())
+ {
+ aRight.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::RIGHT);
+ }
+
+ SwLineEntry aTop (nTop, nLeft, nRight, bTopIsOuter,
+ bVert ? aL : (bBottomAsTop ? aB : aT));
+
+ SwLineEntry aBottom(nBottom, nLeft, nRight, bBottomIsOuter,
+ bVert ? aR : aB);
+
+ Insert( aLeft, false );
+ Insert( aRight, false );
+ Insert( aTop, true );
+ Insert( aBottom, true );
+}
+
+void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
+{
+ // get all lines from structure, that have key entry of pLE
+ SwLineEntryMap* pLine2 = bHori ? &maHoriLines : &maVertLines;
+ const SwTwips nKey = rNew.mnKey;
+ SwLineEntryMap::iterator aMapIter = pLine2->find( nKey );
+
+ SwLineEntrySet* pLineSet = aMapIter != pLine2->end() ? &((*aMapIter).second) : nullptr;
+ if ( !pLineSet )
+ {
+ SwLineEntrySet aNewSet;
+ (*pLine2)[ nKey ] = aNewSet;
+ pLineSet = &(*pLine2)[ nKey ];
+ }
+ SwLineEntrySet::iterator aIter = pLineSet->begin();
+
+ while ( aIter != pLineSet->end() && rNew.mnStartPos < rNew.mnEndPos )
+ {
+ const SwLineEntry& rOld = *aIter;
+
+ if (rOld.mnLimitedEndPos || rOld.mbOuter != rNew.mbOuter)
+ {
+ // Don't merge with this line entry as it ends sooner than mnEndPos.
+ ++aIter;
+ continue;
+ }
+
+ const SwLineEntry::OverlapType nOverlapType = rOld.Overlaps( rNew );
+
+ const svx::frame::Style& rOldAttr = rOld.maAttribute;
+ const svx::frame::Style& rNewAttr = rNew.maAttribute;
+ const svx::frame::Style& rCmpAttr = std::max(rNewAttr, rOldAttr);
+
+ if ( SwLineEntry::OVERLAP1 == nOverlapType )
+ {
+ OSL_ENSURE( rNew.mnStartPos >= rOld.mnStartPos, "Overlap type 3? How this?" );
+
+ // new left segment
+ const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
+
+ // new middle segment
+ const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rOld.mnEndPos, rOld.mbOuter, rCmpAttr);
+
+ // new right segment
+ rNew.mnStartPos = rOld.mnEndPos;
+
+ // update current lines set
+ pLineSet->erase( aIter );
+ if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
+ if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
+
+ aIter = pLineSet->begin();
+
+ continue; // start over
+ }
+ else if ( SwLineEntry::OVERLAP2 == nOverlapType )
+ {
+ // new left segment
+ const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
+
+ // new middle segment
+ const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
+
+ // new right segment
+ const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
+
+ // update current lines set
+ pLineSet->erase( aIter );
+ if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
+ if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
+ if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
+
+ rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
+
+ break; // we are finished
+ }
+ else if ( SwLineEntry::OVERLAP3 == nOverlapType )
+ {
+ // new left segment
+ const SwLineEntry aLeft(nKey, rNew.mnStartPos, rOld.mnStartPos, rOld.mbOuter, rNewAttr);
+
+ // new middle segment
+ const SwLineEntry aMiddle(nKey, rOld.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
+
+ // new right segment
+ const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
+
+ // update current lines set
+ pLineSet->erase( aIter );
+ if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
+ if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
+ if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
+
+ rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
+
+ break; // we are finished
+ }
+
+ ++aIter;
+ }
+
+ if ( rNew.mnStartPos < rNew.mnEndPos ) // insert rest
+ pLineSet->insert( rNew );
+}
+
+/**
+ * FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES END
+ * --> OD #i76669#
+ */
+namespace
+{
+ class SwViewObjectContactRedirector : public sdr::contact::ViewObjectContactRedirector
+ {
+ private:
+ const SwViewShell& mrViewShell;
+
+ public:
+ explicit SwViewObjectContactRedirector( const SwViewShell& rSh )
+ : mrViewShell( rSh )
+ {};
+
+ virtual void createRedirectedPrimitive2DSequence(
+ const sdr::contact::ViewObjectContact& rOriginal,
+ const sdr::contact::DisplayInfo& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override
+ {
+ bool bPaint( true );
+
+ SdrObject* pObj = rOriginal.GetViewContact().TryToGetSdrObject();
+ if ( pObj )
+ {
+ bPaint = SwFlyFrame::IsPaint( pObj, &mrViewShell );
+ }
+
+ if ( !bPaint )
+ {
+ return;
+ }
+
+ sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
+ rOriginal, rDisplayInfo, rVisitor );
+ }
+ };
+
+} // end of anonymous namespace
+// <--
+
+/**
+ * Paint once for every visible page which is touched by Rect
+ *
+ * 1. Paint borders and backgrounds
+ * 2. Paint the draw layer (frames and drawing objects) that is
+ * below the document (hell)
+ * 3. Paint the document content (text)
+ * 4. Paint the draw layer that is above the document
+|*/
+void SwRootFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const pPrintData) const
+{
+ OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "Lower of root is no page." );
+
+ PROTOCOL( this, PROT::FileInit, DbgAction::NONE, nullptr)
+
+ bool bResetRootPaint = false;
+ SwViewShell *pSh = mpCurrShell;
+
+ if ( pSh->GetWin() )
+ {
+ if ( pSh->GetOut() == pSh->GetWin()->GetOutDev() && !pSh->GetWin()->IsVisible() )
+ {
+ return;
+ }
+ if (SwRootFrame::s_isInPaint)
+ {
+ SwPaintQueue::Add( pSh, rRect );
+ return;
+ }
+ }
+ else
+ SwRootFrame::s_isInPaint = bResetRootPaint = true;
+
+ std::unique_ptr<SwSavePaintStatics> pStatics;
+ if ( gProp.pSGlobalShell )
+ pStatics.reset(new SwSavePaintStatics());
+ gProp.pSGlobalShell = pSh;
+
+ if( !pSh->GetWin() )
+ gProp.pSProgress = SfxProgress::GetActiveProgress( static_cast<SfxObjectShell*>(pSh->GetDoc()->GetDocShell()) );
+
+ ::SwCalcPixStatics( pSh->GetOut() );
+ aGlobalRetoucheColor = pSh->Imp()->GetRetoucheColor();
+
+ // Copy rRect; for one, rRect could become dangling during the below action, and for another it
+ // needs to be copied to aRect anyway as that is modified further down below:
+ SwRect aRect( rRect );
+
+ //Trigger an action to clear things up if needed.
+ //Using this trick we can ensure that all values are valid in all paints -
+ //no problems, no special case(s).
+ // #i92745#
+ // Extend check on certain states of the 'current' <SwViewShell> instance to
+ // all existing <SwViewShell> instances.
+ bool bPerformLayoutAction( true );
+ {
+ for(SwViewShell& rTmpViewShell : pSh->GetRingContainer())
+ {
+ if ( rTmpViewShell.IsInEndAction() ||
+ rTmpViewShell.IsPaintInProgress() ||
+ ( rTmpViewShell.Imp()->IsAction() &&
+ rTmpViewShell.Imp()->GetLayAction().IsActionInProgress() ) )
+ {
+ bPerformLayoutAction = false;
+ }
+
+ if(!bPerformLayoutAction)
+ break;
+ }
+ }
+ if ( bPerformLayoutAction )
+ {
+ const_cast<SwRootFrame*>(this)->ResetTurbo();
+ SwLayAction aAction( const_cast<SwRootFrame*>(this), pSh->Imp() );
+ aAction.SetPaint( false );
+ aAction.SetComplete( false );
+ aAction.SetReschedule( gProp.pSProgress != nullptr );
+ aAction.Action(&rRenderContext);
+ ResetTurboFlag();
+ if ( !pSh->ActionPend() )
+ pSh->Imp()->DeletePaintRegion();
+ }
+
+ aRect.Intersection( pSh->VisArea() );
+
+ const bool bExtraData = ::IsExtraData( GetFormat()->GetDoc() );
+
+ gProp.pSLines.reset(new SwLineRects); // Container for borders.
+
+ // #104289#. During painting, something (OLE) can
+ // load the linguistic, which in turn can cause a reformat
+ // of the document. Dangerous! We better set this flag to
+ // avoid the reformat.
+ const bool bOldAction = IsCallbackActionEnabled();
+ const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
+
+ const SwPageFrame *pPage = pSh->Imp()->GetFirstVisPage(&rRenderContext);
+
+ // #126222. The positions of headers and footers of the previous
+ // pages have to be updated, else these headers and footers could
+ // get visible at a wrong position.
+ const SwPageFrame *pPageDeco = static_cast<const SwPageFrame*>(pPage->GetPrev());
+ while (pPageDeco)
+ {
+ pPageDeco->PaintDecorators();
+ OSL_ENSURE(!pPageDeco->GetPrev() || pPageDeco->GetPrev()->IsPageFrame(),
+ "Neighbour of page is not a page.");
+ pPageDeco = static_cast<const SwPageFrame*>(pPageDeco->GetPrev());
+ }
+
+ const bool bBookMode = gProp.pSGlobalShell->GetViewOptions()->IsViewLayoutBookMode();
+ if ( bBookMode && pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
+ pPage = static_cast<const SwPageFrame*>(pPage->GetPrev());
+
+ // #i68597#
+ const bool bGridPainting(pSh->GetWin() && pSh->Imp()->HasDrawView() && pSh->Imp()->GetDrawView()->IsGridVisible());
+
+ // Hide all page break controls before showing them again
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
+ if ( pWrtSh )
+ {
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
+ const SwPageFrame* pHiddenPage = pPage;
+ while ( pHiddenPage->GetPrev() != nullptr )
+ {
+ pHiddenPage = static_cast< const SwPageFrame* >( pHiddenPage->GetPrev() );
+ SwFrameControlPtr pControl = rMngr.GetControl( FrameControlType::PageBreak, pHiddenPage );
+ if ( pControl )
+ pControl->ShowAll( false );
+ }
+ }
+
+ // #i76669#
+ SwViewObjectContactRedirector aSwRedirector( *pSh );
+
+ while ( pPage )
+ {
+ const bool bPaintRightShadow = pPage->IsRightShadowNeeded();
+ const bool bPaintLeftShadow = pPage->IsLeftShadowNeeded();
+ const bool bRightSidebar = pPage->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT;
+
+ if ( !pPage->IsEmptyPage() )
+ {
+ SwRect aPaintRect;
+ SwPageFrame::GetBorderAndShadowBoundRect( pPage->getFrameArea(), pSh, &rRenderContext, aPaintRect,
+ bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
+
+ if ( aRect.Overlaps( aPaintRect ) )
+ {
+ if ( pSh->GetWin() )
+ {
+ gProp.pSSubsLines.reset(new SwSubsRects);
+ gProp.pSSpecSubsLines.reset(new SwSubsRects);
+ }
+ gProp.pBLines.reset(new BorderLines);
+
+ aPaintRect.Intersection_( aRect );
+
+ if ( bExtraData &&
+ pSh->GetWin() && pSh->IsInEndAction() )
+ {
+ // enlarge paint rectangle to complete page width, subtract
+ // current paint area and invalidate the resulting region.
+ SwRectFnSet aRectFnSet(pPage);
+ SwRect aPageRectTemp( aPaintRect );
+ aRectFnSet.SetLeftAndWidth( aPageRectTemp,
+ aRectFnSet.GetLeft(pPage->getFrameArea()),
+ aRectFnSet.GetWidth(pPage->getFrameArea()) );
+ aPageRectTemp.Intersection_( pSh->VisArea() );
+ vcl::Region aPageRectRegion( aPageRectTemp.SVRect() );
+ aPageRectRegion.Exclude( aPaintRect.SVRect() );
+ pSh->GetWin()->Invalidate( aPageRectRegion, InvalidateFlags::Children );
+ }
+
+ // #i80793#
+ // enlarge paint rectangle for objects overlapping the same pixel
+ // in all cases and before the DrawingLayer overlay is initialized.
+ lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
+
+ // #i68597#
+ // moved paint pre-process for DrawingLayer overlay here since the above
+ // code dependent from bExtraData may expand the PaintRect
+ {
+ // #i75172# if called from SwViewShell::ImplEndAction it should no longer
+ // really be used but handled by SwViewShell::ImplEndAction already
+ const vcl::Region aDLRegion(aPaintRect.SVRect());
+ pSh->DLPrePaint2(aDLRegion);
+ }
+
+ if(OUTDEV_WINDOW == gProp.pSGlobalShell->GetOut()->GetOutDevType())
+ {
+ // changed method SwLayVout::Enter(..)
+ // 2nd parameter is no longer <const> and will be set to the
+ // rectangle the virtual output device is calculated from <aPaintRect>,
+ // if the virtual output is used.
+ s_pVout->Enter(pSh, aPaintRect, !s_isNoVirDev);
+
+ // Adjust paint rectangle to pixel size
+ // Thus, all objects overlapping on pixel level with the unadjusted
+ // paint rectangle will be considered in the paint.
+ lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
+ }
+
+ // maybe this can be put in the above scope. Since we are not sure, just leave it ATM
+ s_pVout->SetOrgRect( aPaintRect );
+
+ // determine background color of page for <PaintLayer> method
+ // calls, paint <hell> or <heaven>
+ const Color aPageBackgrdColor(pPage->GetDrawBackgroundColor());
+
+ pPage->PaintBaBo( aPaintRect, pPage );
+
+ if ( pSh->Imp()->HasDrawView() )
+ {
+ gProp.pSLines->LockLines( true );
+ const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
+ pSh->Imp()->PaintLayer( rIDDMA.GetHellId(),
+ pPrintData,
+ *pPage, pPage->getFrameArea(),
+ &aPageBackgrdColor,
+ pPage->IsRightToLeft(),
+ &aSwRedirector );
+ gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
+ gProp.pSLines->LockLines( false );
+ }
+
+ if ( pSh->GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS ) )
+ pPage->PaintBaBo( aPaintRect, pPage, /*bOnlyTextBackground=*/true );
+
+ if( pSh->GetWin() )
+ {
+ // collect sub-lines
+ pPage->RefreshSubsidiary( aPaintRect );
+ // paint special sub-lines
+ gProp.pSSpecSubsLines->PaintSubsidiary( pSh->GetOut(), nullptr, gProp );
+ }
+
+ pPage->PaintSwFrame( rRenderContext, aPaintRect );
+
+ // no paint of page border and shadow, if writer is in place mode.
+ if( pSh->GetWin() && pSh->GetDoc()->GetDocShell() &&
+ !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
+ {
+ SwPageFrame::PaintBorderAndShadow( pPage->getFrameArea(), pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
+ SwPageFrame::PaintNotesSidebar( pPage->getFrameArea(), pSh, pPage->GetPhyPageNum(), bRightSidebar);
+ }
+
+ gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
+ if ( pSh->GetWin() )
+ {
+ gProp.pSSubsLines->PaintSubsidiary( pSh->GetOut(), gProp.pSLines.get(), gProp );
+ gProp.pSSubsLines.reset();
+ gProp.pSSpecSubsLines.reset();
+ }
+ // fdo#42750: delay painting these until after subsidiary lines
+ // fdo#45562: delay painting these until after hell layer
+ // fdo#47717: but do it before heaven layer
+ ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
+
+ if ( pSh->Imp()->HasDrawView() )
+ {
+ pSh->Imp()->PaintLayer( pSh->GetDoc()->getIDocumentDrawModelAccess().GetHeavenId(),
+ pPrintData,
+ *pPage, pPage->getFrameArea(),
+ &aPageBackgrdColor,
+ pPage->IsRightToLeft(),
+ &aSwRedirector );
+ }
+
+ if ( bExtraData )
+ pPage->RefreshExtraData( aPaintRect );
+
+ gProp.pBLines.reset();
+ s_pVout->Leave();
+
+ // #i68597#
+ // needed to move grid painting inside Begin/EndDrawLayer bounds and to change
+ // output rect for it accordingly
+ if(bGridPainting)
+ {
+ SdrPaintView* pPaintView = pSh->Imp()->GetDrawView();
+ SdrPageView* pPageView = pPaintView->GetSdrPageView();
+ pPageView->DrawPageViewGrid(*pSh->GetOut(), aPaintRect.SVRect(), SwViewOption::GetTextGridColor() );
+ }
+
+ // #i68597#
+ // moved paint post-process for DrawingLayer overlay here, see above
+ {
+ pSh->DLPostPaint2(true);
+ }
+ }
+
+ pPage->PaintDecorators( );
+ pPage->PaintBreak();
+ }
+ else if ( bBookMode && pSh->GetWin() && !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
+ {
+ // paint empty page
+ SwRect aPaintRect;
+ SwRect aEmptyPageRect( pPage->getFrameArea() );
+
+ // code from vprint.cxx
+ const SwPageFrame& rFormatPage = pPage->GetFormatPage();
+ aEmptyPageRect.SSize( rFormatPage.getFrameArea().SSize() );
+
+ SwPageFrame::GetBorderAndShadowBoundRect( aEmptyPageRect, pSh, &rRenderContext, aPaintRect,
+ bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
+ aPaintRect.Intersection_( aRect );
+
+ if ( aRect.Overlaps( aEmptyPageRect ) )
+ {
+ // #i75172# if called from SwViewShell::ImplEndAction it should no longer
+ // really be used but handled by SwViewShell::ImplEndAction already
+ {
+ const vcl::Region aDLRegion(aPaintRect.SVRect());
+ pSh->DLPrePaint2(aDLRegion);
+ }
+
+ if( pSh->GetOut()->GetFillColor() != aGlobalRetoucheColor )
+ pSh->GetOut()->SetFillColor( aGlobalRetoucheColor );
+ // No line color
+ pSh->GetOut()->SetLineColor();
+ // Use aligned page rectangle
+ {
+ SwRect aTmpPageRect( aEmptyPageRect );
+ ::SwAlignRect( aTmpPageRect, pSh, &rRenderContext );
+ aEmptyPageRect = aTmpPageRect;
+ }
+
+ pSh->GetOut()->DrawRect( aEmptyPageRect.SVRect() );
+
+ // paint empty page text
+ const vcl::Font& rEmptyPageFont = SwPageFrame::GetEmptyPageFont();
+ const vcl::Font aOldFont( pSh->GetOut()->GetFont() );
+
+ pSh->GetOut()->SetFont( rEmptyPageFont );
+ pSh->GetOut()->DrawText( aEmptyPageRect.SVRect(), SwResId( STR_EMPTYPAGE ),
+ DrawTextFlags::VCenter |
+ DrawTextFlags::Center |
+ DrawTextFlags::Clip );
+
+ pSh->GetOut()->SetFont( aOldFont );
+ // paint shadow and border for empty page
+ SwPageFrame::PaintBorderAndShadow( aEmptyPageRect, pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
+ SwPageFrame::PaintNotesSidebar( aEmptyPageRect, pSh, pPage->GetPhyPageNum(), bRightSidebar);
+
+ {
+ pSh->DLPostPaint2(true);
+ }
+ }
+ }
+
+ OSL_ENSURE( !pPage->GetNext() || pPage->GetNext()->IsPageFrame(),
+ "Neighbour of page is not a page." );
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+
+ gProp.pSLines.reset();
+
+ if ( bResetRootPaint )
+ SwRootFrame::s_isInPaint = false;
+ if ( pStatics )
+ pStatics.reset();
+ else
+ {
+ gProp.pSProgress = nullptr;
+ gProp.pSGlobalShell = nullptr;
+ }
+
+ const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
+}
+
+static void lcl_EmergencyFormatFootnoteCont( SwFootnoteContFrame *pCont )
+{
+ vcl::RenderContext* pRenderContext = pCont->getRootFrame()->GetCurrShell()->GetOut();
+
+ //It's possible that the Cont will get destroyed.
+ SwContentFrame *pCnt = pCont->ContainsContent();
+ while ( pCnt && pCnt->IsInFootnote() )
+ {
+ pCnt->Calc(pRenderContext);
+ pCnt = pCnt->GetNextContentFrame();
+ }
+}
+
+namespace {
+
+class SwShortCut
+{
+ SwRectDist m_fnCheck;
+ tools::Long m_nLimit;
+
+public:
+ SwShortCut( const SwFrame& rFrame, const SwRect& rRect );
+ bool Stop(const SwRect& rRect) const { return (rRect.*m_fnCheck)(m_nLimit) > 0; }
+};
+
+}
+
+SwShortCut::SwShortCut( const SwFrame& rFrame, const SwRect& rRect )
+{
+ bool bVert = rFrame.IsVertical();
+ bool bR2L = rFrame.IsRightToLeft();
+ if( rFrame.IsNeighbourFrame() && bVert == bR2L )
+ {
+ if( bVert )
+ {
+ m_fnCheck = &SwRect::GetBottomDistance;
+ m_nLimit = rRect.Top();
+ }
+ else
+ {
+ m_fnCheck = &SwRect::GetLeftDistance;
+ m_nLimit = rRect.Left() + rRect.Width();
+ }
+ }
+ else if( bVert == rFrame.IsNeighbourFrame() )
+ {
+ m_fnCheck = &SwRect::GetTopDistance;
+ m_nLimit = rRect.Top() + rRect.Height();
+ }
+ else
+ {
+ if ( rFrame.IsVertLR() )
+ {
+ m_fnCheck = &SwRect::GetLeftDistance;
+ m_nLimit = rRect.Right();
+ }
+ else
+ {
+ m_fnCheck = &SwRect::GetRightDistance;
+ m_nLimit = rRect.Left();
+ }
+ }
+}
+
+void SwLayoutFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ // #i16816# tagged pdf support
+ Frame_Info aFrameInfo( *this );
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, &aFrameInfo, nullptr, rRenderContext );
+
+ const SwFrame *pFrame = Lower();
+ if ( !pFrame )
+ return;
+
+ SwFrameDeleteGuard g(const_cast<SwLayoutFrame*>(this)); // lock because Calc() and recursion
+ SwShortCut aShortCut( *pFrame, rRect );
+ bool bCnt = pFrame->IsContentFrame();
+ if ( bCnt )
+ pFrame->Calc(&rRenderContext);
+
+ if ( pFrame->IsFootnoteContFrame() )
+ {
+ ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame)) );
+ pFrame = Lower();
+ }
+
+ const SwPageFrame *pPage = nullptr;
+ bool bWin = gProp.pSGlobalShell->GetWin() != nullptr;
+ if (comphelper::LibreOfficeKit::isTiledPainting())
+ // Tiled rendering is similar to printing in this case: painting transparently multiple
+ // times will result in darker colors: avoid that.
+ bWin = false;
+
+ while ( IsAnLower( pFrame ) )
+ {
+ SwRect aPaintRect( pFrame->GetPaintArea() );
+ if( aShortCut.Stop( aPaintRect ) )
+ break;
+ if ( bCnt && gProp.pSProgress )
+ SfxProgress::Reschedule();
+
+ //We need to retouch if a frame explicitly requests it.
+ //First do the retouch, because this could flatten the borders.
+ if ( pFrame->IsRetouche() )
+ {
+ if ( pFrame->IsRetoucheFrame() && bWin && !pFrame->GetNext() )
+ {
+ if ( !pPage )
+ pPage = FindPageFrame();
+ pFrame->Retouch( pPage, rRect );
+ }
+ pFrame->ResetRetouche();
+ }
+
+ if ( rRect.Overlaps( aPaintRect ) )
+ {
+ if ( bCnt && pFrame->IsCompletePaint() &&
+ !rRect.Contains( aPaintRect ) && Application::AnyInput( VclInputFlags::KEYBOARD ) )
+ {
+ //fix(8104): It may happen, that the processing wasn't complete
+ //but some parts of the paragraph were still repainted.
+ //This could lead to the situation, that other parts of the
+ //paragraph won't be repainted at all. The only solution seems
+ //to be an invalidation of the window.
+ //To not make it too severe the rectangle is limited by
+ //painting the desired part and only invalidating the
+ //remaining paragraph parts.
+ if ( aPaintRect.Left() == rRect.Left() &&
+ aPaintRect.Right() == rRect.Right() )
+ {
+ aPaintRect.Bottom( rRect.Top() - 1 );
+ if ( aPaintRect.Height() > 0 )
+ gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
+ aPaintRect.Top( rRect.Bottom() + 1 );
+ aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
+ if ( aPaintRect.Height() > 0 )
+ gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
+ aPaintRect.Top( pFrame->getFrameArea().Top() );
+ aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
+ }
+ else
+ {
+ gProp.pSGlobalShell->InvalidateWindows( aPaintRect );
+ pFrame = pFrame->GetNext();
+ if ( pFrame )
+ {
+ bCnt = pFrame->IsContentFrame();
+ if ( bCnt )
+ pFrame->Calc(&rRenderContext);
+ }
+ continue;
+ }
+ }
+ pFrame->ResetCompletePaint();
+ aPaintRect.Intersection_( rRect );
+
+ pFrame->PaintSwFrame( rRenderContext, aPaintRect );
+
+ if ( Lower() && Lower()->IsColumnFrame() )
+ {
+ //Paint the column separator line if needed. The page is
+ //responsible for the page frame - not the upper.
+ const SwFrameFormat *pFormat = GetUpper() && GetUpper()->IsPageFrame()
+ ? GetUpper()->GetFormat()
+ : GetFormat();
+ const SwFormatCol &rCol = pFormat->GetCol();
+ if ( rCol.GetLineAdj() != COLADJ_NONE )
+ {
+ if ( !pPage )
+ pPage = pFrame->FindPageFrame();
+
+ PaintColLines( aPaintRect, rCol, pPage );
+ }
+ }
+ }
+ if ( !bCnt && pFrame->GetNext() && pFrame->GetNext()->IsFootnoteContFrame() )
+ ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame->GetNext())) );
+
+ pFrame = pFrame->GetNext();
+
+ if ( pFrame )
+ {
+ bCnt = pFrame->IsContentFrame();
+ if ( bCnt )
+ pFrame->Calc(&rRenderContext);
+ }
+ }
+}
+
+static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateDashedIndicatorPrimitive(
+ const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd,
+ basegfx::BColor aColor )
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSeq( 1 );
+
+ std::vector< double > aStrokePattern;
+ basegfx::B2DPolygon aLinePolygon;
+ aLinePolygon.append(rStart);
+ aLinePolygon.append(rEnd);
+
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ if ( rSettings.GetHighContrastMode( ) )
+ {
+ // Only a solid line in high contrast mode
+ aColor = rSettings.GetDialogTextColor().getBColor();
+ }
+ else
+ {
+ // Get a color for the contrast
+ basegfx::BColor aHslLine = basegfx::utils::rgb2hsl( aColor );
+ double nLuminance = aHslLine.getZ() * 2.5;
+ if ( nLuminance == 0 )
+ nLuminance = 0.5;
+ else if ( nLuminance >= 1.0 )
+ nLuminance = aHslLine.getZ() * 0.4;
+ aHslLine.setZ( nLuminance );
+ const basegfx::BColor aOtherColor = basegfx::utils::hsl2rgb( aHslLine );
+
+ // Compute the plain line
+ aSeq[0] =
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aLinePolygon, aOtherColor );
+
+ // Dashed line in twips
+ aStrokePattern.push_back( 40 );
+ aStrokePattern.push_back( 40 );
+
+ aSeq.resize( 2 );
+ }
+
+ // Compute the dashed line primitive
+ aSeq[ aSeq.size( ) - 1 ] =
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D (
+ basegfx::B2DPolyPolygon( aLinePolygon ),
+ drawinglayer::attribute::LineAttribute( aColor ),
+ drawinglayer::attribute::StrokeAttribute( std::move(aStrokePattern) ) );
+
+
+ return aSeq;
+}
+
+void SwPageFrame::PaintBreak( ) const
+{
+ if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
+ gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
+ gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
+ gProp.pSGlobalShell->IsPreview() )
+ return;
+
+ const SwFrame* pBodyFrame = Lower();
+ while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
+ pBodyFrame = pBodyFrame->GetNext();
+
+ if ( pBodyFrame )
+ {
+ const SwLayoutFrame* pLayBody = static_cast< const SwLayoutFrame* >( pBodyFrame );
+ const SwFlowFrame *pFlowFrame = pLayBody->ContainsContent();
+
+ // Test if the first node is a table
+ const SwFrame* pFirstFrame = pLayBody->Lower();
+ if ( pFirstFrame && pFirstFrame->IsTabFrame() )
+ pFlowFrame = static_cast< const SwTabFrame* >( pFirstFrame );
+
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
+ if ( pWrtSh )
+ {
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
+
+ if ( pFlowFrame && pFlowFrame->IsPageBreak( true ) )
+ rMngr.SetPageBreakControl( this );
+ else
+ rMngr.RemoveControlsByType( FrameControlType::PageBreak, this );
+ }
+ }
+ SwLayoutFrame::PaintBreak( );
+}
+
+void SwColumnFrame::PaintBreak( ) const
+{
+ if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
+ gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
+ gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
+ gProp.pSGlobalShell->IsPreview() )
+ return;
+
+ const SwFrame* pBodyFrame = Lower();
+ while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
+ pBodyFrame = pBodyFrame->GetNext();
+
+ if ( !pBodyFrame )
+ return;
+
+ const SwContentFrame *pCnt = static_cast< const SwLayoutFrame* >( pBodyFrame )->ContainsContent();
+ if ( !(pCnt && pCnt->IsColBreak( true )) )
+ return;
+
+ // Paint the break only if:
+ // * Not in header footer edition, to avoid conflicts with the
+ // header/footer marker
+ // * Non-printing characters are shown, as this is more consistent
+ // with other formatting marks
+ if ( !(!gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) &&
+ !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) &&
+ gProp.pSGlobalShell->GetViewOptions()->IsLineBreak()) )
+ return;
+
+ SwRect aRect( pCnt->getFramePrintArea() );
+ aRect.Pos() += pCnt->getFrameArea().Pos();
+
+ // Draw the line
+ basegfx::B2DPoint aStart( double( aRect.Left() ), aRect.Top() );
+ basegfx::B2DPoint aEnd( double( aRect.Right() ), aRect.Top() );
+ double nWidth = aRect.Width();
+ if ( IsVertical( ) )
+ {
+ aStart = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Top() ) );
+ aEnd = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Bottom() ) );
+ nWidth = aRect.Height();
+ }
+
+ basegfx::BColor aLineColor = SwViewOption::GetPageBreakColor().getBColor();
+
+ drawinglayer::primitive2d::Primitive2DContainer aSeq =
+ lcl_CreateDashedIndicatorPrimitive( aStart, aEnd, aLineColor );
+
+ // Add the text above
+ OUString aBreakText = SwResId(STR_COLUMN_BREAK);
+
+ basegfx::B2DVector aFontSize;
+ OutputDevice* pOut = gProp.pSGlobalShell->GetOut();
+ vcl::Font aFont = pOut->GetSettings().GetStyleSettings().GetToolFont();
+ aFont.SetFontHeight( 8 * 20 );
+ pOut->SetFont( aFont );
+ drawinglayer::attribute::FontAttribute aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont(
+ aFontSize, aFont, IsRightToLeft(), false );
+
+ tools::Rectangle aTextRect;
+ pOut->GetTextBoundRect( aTextRect, aBreakText );
+ tools::Long nTextOff = ( nWidth - aTextRect.GetWidth() ) / 2;
+
+ basegfx::B2DHomMatrix aTextMatrix( basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aFontSize.getX(), aFontSize.getY(),
+ aRect.Left() + nTextOff, aRect.Top() ) );
+ if ( IsVertical() )
+ {
+ aTextMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix (
+ aFontSize.getX(), aFontSize.getY(), 0.0, M_PI_2,
+ aRect.Right(), aRect.Top() + nTextOff );
+ }
+
+ aSeq.push_back(
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aTextMatrix,
+ aBreakText, 0, aBreakText.getLength(),
+ std::vector< double >(),
+ aFontAttr,
+ lang::Locale(),
+ aLineColor ) );
+
+ ProcessPrimitives( aSeq );
+}
+
+void SwLayoutFrame::PaintBreak( ) const
+{
+ const SwFrame* pFrame = Lower();
+ while ( pFrame )
+ {
+ if ( pFrame->IsLayoutFrame() )
+ static_cast< const SwLayoutFrame*>( pFrame )->PaintBreak( );
+ pFrame = pFrame->GetNext();
+ }
+}
+
+void SwPageFrame::PaintDecorators( ) const
+{
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
+ if ( !pWrtSh )
+ return;
+
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+
+ const SwLayoutFrame* pBody = FindBodyCont();
+ if ( !pBody )
+ return;
+
+ SwRect aBodyRect( pBody->getFrameArea() );
+
+ if ( !(gProp.pSGlobalShell->GetOut()->GetOutDevType() != OUTDEV_PRINTER &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() &&
+ !gProp.pSGlobalShell->IsPreview() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
+ !gProp.pSGlobalShell->GetViewOptions()->getBrowseMode() &&
+ ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) ||
+ gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )) )
+ return;
+
+ bool bRtl = AllSettings::GetLayoutRTL();
+ const SwRect& rVisArea = gProp.pSGlobalShell->VisArea();
+ tools::Long nXOff = std::min( aBodyRect.Right(), rVisArea.Right() );
+ if ( bRtl )
+ nXOff = std::max( aBodyRect.Left(), rVisArea.Left() );
+
+ // Header
+ if ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) )
+ {
+ const SwFrame* pHeaderFrame = Lower();
+ if ( !pHeaderFrame->IsHeaderFrame() )
+ pHeaderFrame = nullptr;
+
+ tools::Long nHeaderYOff = aBodyRect.Top();
+ Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nHeaderYOff ) );
+ rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, FrameControlType::Header, nOutputOff );
+ }
+
+ // Footer
+ if ( !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )
+ return;
+
+ const SwFrame* pFootnoteContFrame = Lower();
+ while ( pFootnoteContFrame )
+ {
+ if ( pFootnoteContFrame->IsFootnoteContFrame() )
+ aBodyRect.AddBottom( pFootnoteContFrame->getFrameArea().Bottom() - aBodyRect.Bottom() );
+ pFootnoteContFrame = pFootnoteContFrame->GetNext();
+ }
+
+ tools::Long nFooterYOff = aBodyRect.Bottom();
+ Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nFooterYOff ) );
+ rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, FrameControlType::Footer, nOutputOff );
+}
+
+/**
+ * For feature #99657#
+ *
+ * OD 12.08.2002
+ * determines, if background of fly frame has to be drawn transparent
+ * declaration found in /core/inc/flyfrm.cxx
+ *
+ * OD 08.10.2002 #103898# - If the background of the fly frame itself is not
+ * transparent and the background is inherited from its parent/grandparent,
+ * the background brush, used for drawing, has to be investigated for transparency.
+ *
+ * @return true, if background is transparent drawn
+*/
+bool SwFlyFrame::IsBackgroundTransparent() const
+{
+ bool bBackgroundTransparent = GetFormat()->IsBackgroundTransparent();
+ if ( !bBackgroundTransparent &&
+ GetFormat()->IsBackgroundBrushInherited() )
+ {
+ const SvxBrushItem* pBackgroundBrush = nullptr;
+ std::optional<Color> xSectionTOXColor;
+ SwRect aDummyRect;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ if ( GetBackgroundBrush( aFillAttributes, pBackgroundBrush, xSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false) )
+ {
+ if ( xSectionTOXColor &&
+ (xSectionTOXColor->IsTransparent()) &&
+ (xSectionTOXColor != COL_TRANSPARENT) )
+ {
+ bBackgroundTransparent = true;
+ }
+ else if(aFillAttributes && aFillAttributes->isUsed())
+ {
+ bBackgroundTransparent = aFillAttributes->isTransparent();
+ }
+ else if ( pBackgroundBrush )
+ {
+ if ( (pBackgroundBrush->GetColor().IsTransparent()) &&
+ (pBackgroundBrush->GetColor() != COL_TRANSPARENT) )
+ {
+ bBackgroundTransparent = true;
+ }
+ else
+ {
+ const GraphicObject *pTmpGrf =
+ pBackgroundBrush->GetGraphicObject();
+ if ( pTmpGrf &&
+ (pTmpGrf->GetAttr().IsTransparent())
+ )
+ {
+ bBackgroundTransparent = true;
+ }
+ }
+ }
+ }
+ }
+
+ return bBackgroundTransparent;
+};
+
+bool SwFlyFrame::IsPaint( SdrObject *pObj, const SwViewShell *pSh )
+{
+ SdrObjUserCall *pUserCall = GetUserCall(pObj);
+
+ if ( nullptr == pUserCall )
+ return true;
+
+ //Attribute dependent, don't paint for printer or Preview
+ bool bPaint = gProp.pSFlyOnlyDraw ||
+ static_cast<SwContact*>(pUserCall)->GetFormat()->GetPrint().GetValue();
+ if ( !bPaint )
+ bPaint = pSh->GetWin() && !pSh->IsPreview();
+
+ if ( bPaint )
+ {
+ //The paint may be prevented by the superior Flys.
+ SwFrame *pAnch = nullptr;
+ if ( dynamic_cast< const SwFlyDrawObj *>( pObj ) != nullptr ) // i#117962#
+ {
+ bPaint = false;
+ }
+ if ( auto pFlyDraw = dynamic_cast<SwVirtFlyDrawObj *>( pObj ) )
+ {
+ SwFlyFrame *pFly = pFlyDraw->GetFlyFrame();
+ if ( gProp.pSFlyOnlyDraw && gProp.pSFlyOnlyDraw == pFly )
+ return true;
+
+ //Try to avoid displaying the intermediate stage, Flys which don't
+ //overlap with the page on which they are anchored won't be
+ //painted.
+ //HACK: exception: printing of frames in tables, those can overlap
+ //a page once in a while when dealing with oversized tables (HTML).
+ SwPageFrame *pPage = pFly->FindPageFrame();
+ if ( pPage && pPage->getFrameArea().Overlaps( pFly->getFrameArea() ) )
+ {
+ pAnch = pFly->AnchorFrame();
+ }
+
+ }
+ else
+ {
+ // Consider 'virtual' drawing objects
+ SwDrawContact* pDrawContact = dynamic_cast<SwDrawContact*>(pUserCall);
+ pAnch = pDrawContact ? pDrawContact->GetAnchorFrame(pObj) : nullptr;
+ if ( pAnch )
+ {
+ if ( !pAnch->isFrameAreaPositionValid() )
+ pAnch = nullptr;
+ else if ( pSh->GetOut() == pSh->getIDocumentDeviceAccess().getPrinter( false ))
+ {
+ //HACK: we have to omit some of the objects for printing,
+ //otherwise they would be printed twice.
+ //The objects should get printed if the TableHack is active
+ //right now. Afterwards they must not be printed if the
+ //page over which they float position wise gets printed.
+ const SwPageFrame *pPage = pAnch->FindPageFrame();
+ if ( !pPage->getFrameArea().Overlaps( SwRect(pObj->GetCurrentBoundRect()) ) )
+ pAnch = nullptr;
+ }
+ }
+ else
+ {
+ if ( dynamic_cast< const SdrObjGroup *>( pObj ) == nullptr )
+ {
+ OSL_FAIL( "<SwFlyFrame::IsPaint(..)> - paint of drawing object without anchor frame!?" );
+ }
+ }
+ }
+ if ( pAnch )
+ {
+ if ( pAnch->IsInFly() )
+ bPaint = SwFlyFrame::IsPaint( pAnch->FindFlyFrame()->GetVirtDrawObj(),
+ pSh );
+ else if ( gProp.pSFlyOnlyDraw )
+ bPaint = false;
+ }
+ else
+ bPaint = false;
+ }
+ return bPaint;
+}
+
+void SwCellFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ if ( GetLayoutRowSpan() >= 1 )
+ SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
+}
+
+namespace {
+
+struct BorderLinesGuard
+{
+ explicit BorderLinesGuard() : m_pBorderLines(std::move(gProp.pBLines))
+ {
+ gProp.pBLines.reset(new BorderLines);
+ }
+ ~BorderLinesGuard()
+ {
+ gProp.pBLines = std::move(m_pBorderLines);
+ }
+private:
+ std::unique_ptr<BorderLines> m_pBorderLines;
+};
+
+}
+
+// set strikethrough for deleted objects anchored to character
+void SwFrame::SetDrawObjsAsDeleted( bool bDeleted )
+{
+ if ( SwSortedObjs *pObjs = GetDrawObjs() )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ pFly->SetDeleted(bDeleted);
+ }
+ }
+ }
+}
+
+void SwFlyFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ //optimize thumbnail generation and store procedure to improve odt saving performance, #i120030#
+ SwViewShell *pShell = getRootFrame()->GetCurrShell();
+ if (pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocShell())
+ {
+ bool bInGenerateThumbnail = pShell->GetDoc()->GetDocShell()->IsInGenerateAndStoreThumbnail();
+ if (bInGenerateThumbnail)
+ {
+ const SwRect& aVisRect = pShell->VisArea();
+ if (!aVisRect.Overlaps(getFrameArea()))
+ return;
+ }
+ }
+
+ //because of the overlapping of frames and drawing objects the flys have to
+ //paint their borders (and those of the internal ones) directly.
+ //e.g. #33066#
+ gProp.pSLines->LockLines(true);
+ BorderLinesGuard blg; // this should not paint borders added from PaintBaBo
+
+ SwRect aRect( rRect );
+ aRect.Intersection_( getFrameArea() );
+
+ rRenderContext.Push( vcl::PushFlags::CLIPREGION );
+ rRenderContext.SetClipRegion();
+ const SwPageFrame* pPage = FindPageFrame();
+
+ const SwNoTextFrame *pNoText = Lower() && Lower()->IsNoTextFrame()
+ ? static_cast<const SwNoTextFrame*>(Lower()) : nullptr;
+
+ bool bIsChart = false; //#i102950# don't paint additional borders for charts
+ //check whether we have a chart
+ if(pNoText)
+ {
+ const SwNoTextNode* pNoTNd = dynamic_cast<const SwNoTextNode*>(pNoText->GetNode());
+ if( pNoTNd )
+ {
+ SwOLENode* pOLENd = const_cast<SwOLENode*>(pNoTNd->GetOLENode());
+ if( pOLENd && pOLENd->GetOLEObj().GetObject().IsChart() )
+ bIsChart = true;
+ }
+ }
+
+ {
+ bool bContour = GetFormat()->GetSurround().IsContour();
+ tools::PolyPolygon aPoly;
+ if ( bContour )
+ {
+ // add 2nd parameter with value <true>
+ // to indicate that method is called for paint in order to avoid
+ // load of the intrinsic graphic.
+ bContour = GetContour( aPoly, true );
+ }
+
+ // #i47804# - distinguish complete background paint
+ // and margin paint.
+ // paint complete background for Writer text fly frames
+ bool bPaintCompleteBack( !pNoText );
+ // paint complete background for transparent graphic and contour,
+ // if own background color exists.
+ const bool bIsGraphicTransparent = pNoText && pNoText->IsTransparent();
+ if ( !bPaintCompleteBack &&
+ ( bIsGraphicTransparent|| bContour ) )
+ {
+ const SwFlyFrameFormat* pSwFrameFormat = GetFormat();
+
+ if (pSwFrameFormat && pSwFrameFormat->supportsFullDrawingLayerFillAttributeSet())
+ {
+ // check for transparency
+ const drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(pSwFrameFormat->getSdrAllFillAttributesHelper());
+
+ // check if the new fill attributes are used
+ if(aFillAttributes && aFillAttributes->isUsed())
+ {
+ bPaintCompleteBack = true;
+ }
+ }
+ else
+ {
+ std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
+ // to determine, if background has to be painted, by checking, if
+ // background color is not COL_TRANSPARENT ("no fill"/"auto fill")
+ // or a background graphic exists.
+ bPaintCompleteBack =
+ aBack->GetColor() != COL_TRANSPARENT ||
+ aBack->GetGraphicPos() != GPOS_NONE;
+ }
+ }
+ // paint of margin needed.
+ const bool bPaintMarginOnly( !bPaintCompleteBack &&
+ getFramePrintArea().SSize() != getFrameArea().SSize() );
+
+ // #i47804# - paint background of parent fly frame
+ // for transparent graphics in layer Hell, if parent fly frame isn't
+ // in layer Hell. It's only painted the intersection between the
+ // parent fly frame area and the paint area <aRect>
+ const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
+
+ if (bIsGraphicTransparent &&
+ GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS) &&
+ GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() &&
+ GetAnchorFrame()->FindFlyFrame() )
+ {
+ const SwFlyFrame* pParentFlyFrame = GetAnchorFrame()->FindFlyFrame();
+ if ( pParentFlyFrame->GetDrawObj()->GetLayer() !=
+ rIDDMA.GetHellId() )
+ {
+ SwFlyFrame* pOldRet = gProp.pSRetoucheFly2;
+ gProp.pSRetoucheFly2 = const_cast<SwFlyFrame*>(this);
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pParentFlyFrame );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ SwRect aPaintRect( aRect );
+ aPaintRect.Intersection_( pParentFlyFrame->getFrameArea() );
+ pParentFlyFrame->PaintSwFrameBackground( aPaintRect, pPage, rAttrs );
+
+ gProp.pSRetoucheFly2 = pOldRet;
+ }
+ }
+
+ if ( bPaintCompleteBack || bPaintMarginOnly )
+ {
+ //#24926# JP 01.02.96, PaintBaBo is here partially so PaintSwFrameShadowAndBorder
+ //receives the original Rect but PaintSwFrameBackground only the limited
+ //one.
+
+ rRenderContext.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ rRenderContext.SetLineColor();
+
+ pPage = FindPageFrame();
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ // paint background
+ {
+ SwRegionRects aRegion( aRect );
+ // #i80822#
+ // suppress painting of background in printing area for
+ // non-transparent graphics.
+ if ( bPaintMarginOnly ||
+ ( pNoText && !bIsGraphicTransparent ) )
+ {
+ //What we actually want to paint is the small stripe between
+ //PrtArea and outer border.
+ SwRect aTmp( getFramePrintArea() ); aTmp += getFrameArea().Pos();
+ aRegion -= aTmp;
+ }
+ if ( bContour )
+ {
+ rRenderContext.Push();
+ // #i80822#
+ // apply clip region under the same conditions, which are
+ // used in <SwNoTextFrame::PaintSwFrame(..)> to set the clip region
+ // for painting the graphic/OLE. Thus, the clip region is
+ // also applied for the PDF export.
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+
+ if ( !rRenderContext.GetConnectMetaFile() || !pSh || !pSh->GetWin() )
+ {
+ rRenderContext.SetClipRegion(vcl::Region(aPoly));
+ }
+
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ {
+ PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
+ }
+
+ rRenderContext.Pop();
+ }
+ else
+ {
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ {
+ PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
+ }
+ }
+ }
+
+ // paint border before painting background
+ PaintSwFrameShadowAndBorder(rRect, pPage, rAttrs);
+
+ rRenderContext.Pop();
+ }
+ }
+
+ // fly frame will paint it's subsidiary lines and
+ // the subsidiary lines of its lowers on its own, due to overlapping with
+ // other fly frames or other objects.
+ if( gProp.pSGlobalShell->GetWin()
+ && !bIsChart ) //#i102950# don't paint additional borders for charts
+ {
+ bool bSubsLineRectsCreated;
+ if ( gProp.pSSubsLines )
+ {
+ // Lock already existing subsidiary lines
+ gProp.pSSubsLines->LockLines( true );
+ bSubsLineRectsCreated = false;
+ }
+ else
+ {
+ // create new subsidiary lines
+ gProp.pSSubsLines.reset(new SwSubsRects);
+ bSubsLineRectsCreated = true;
+ }
+
+ bool bSpecSubsLineRectsCreated;
+ if ( gProp.pSSpecSubsLines )
+ {
+ // Lock already existing special subsidiary lines
+ gProp.pSSpecSubsLines->LockLines( true );
+ bSpecSubsLineRectsCreated = false;
+ }
+ else
+ {
+ // create new special subsidiary lines
+ gProp.pSSpecSubsLines.reset(new SwSubsRects);
+ bSpecSubsLineRectsCreated = true;
+ }
+ // Add subsidiary lines of fly frame and its lowers
+ RefreshLaySubsidiary( pPage, aRect );
+ // paint subsidiary lines of fly frame and its lowers
+ gProp.pSSpecSubsLines->PaintSubsidiary( &rRenderContext, nullptr, gProp );
+ gProp.pSSubsLines->PaintSubsidiary(&rRenderContext, gProp.pSLines.get(), gProp);
+ if ( !bSubsLineRectsCreated )
+ // unlock subsidiary lines
+ gProp.pSSubsLines->LockLines( false );
+ else
+ {
+ // delete created subsidiary lines container
+ gProp.pSSubsLines.reset();
+ }
+
+ if ( !bSpecSubsLineRectsCreated )
+ // unlock special subsidiary lines
+ gProp.pSSpecSubsLines->LockLines( false );
+ else
+ {
+ // delete created special subsidiary lines container
+ gProp.pSSpecSubsLines.reset();
+ }
+ }
+
+ SwLayoutFrame::PaintSwFrame( rRenderContext, aRect );
+
+ Validate();
+
+ {
+ SwTaggedPDFHelper tag(nullptr, nullptr, nullptr, rRenderContext);
+ // first paint lines added by fly frame paint
+ // and then unlock other lines.
+ gProp.pSLines->PaintLines( &rRenderContext, gProp );
+ gProp.pSLines->LockLines( false );
+ // have to paint frame borders added in heaven layer here...
+ ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
+ }
+
+ PaintDecorators();
+
+ // crossing out for tracked deletion
+ if ( GetAuthor() != std::string::npos && IsDeleted() )
+ {
+ tools::Long startX = aRect.Left( ), endX = aRect.Right();
+ tools::Long startY = aRect.Top( ), endY = aRect.Bottom();
+ rRenderContext.SetLineColor( SwPostItMgr::GetColorAnchor(GetAuthor()) );
+ rRenderContext.DrawLine(Point(startX, startY), Point(endX, endY));
+ rRenderContext.DrawLine(Point(startX, endY), Point(endX, startY));
+ }
+
+ rRenderContext.Pop();
+
+ if ( gProp.pSProgress && pNoText )
+ SfxProgress::Reschedule();
+}
+
+void SwFlyFrame::PaintDecorators() const
+{
+ // Show the un-float button
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
+ if ( pWrtSh )
+ {
+ UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
+ }
+}
+
+void SwTextFrame::PaintOutlineContentVisibilityButton() const
+{
+ SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
+ if (pWrtSh && pWrtSh->GetViewOptions()->IsShowOutlineContentVisibilityButton())
+ UpdateOutlineContentVisibilityButton(pWrtSh);
+}
+
+
+void SwTabFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ const SwViewOption* pViewOption = gProp.pSGlobalShell->GetViewOptions();
+ if (pViewOption->IsTable())
+ {
+ // #i29550#
+ if ( IsCollapsingBorders() )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ // paint shadow
+ if ( rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
+ {
+ SwRect aRect;
+ ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
+ PaintShadow( rRect, aRect, rAttrs );
+ }
+
+ SwTabFramePainter aHelper(*this);
+ aHelper.PaintLines(rRenderContext, rRect);
+ }
+
+ SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
+ }
+ // #i6467# - no light grey rectangle for page preview
+ else if ( gProp.pSGlobalShell->GetWin() && !gProp.pSGlobalShell->IsPreview() )
+ {
+ // #i6467# - intersect output rectangle with table frame
+ SwRect aTabRect( getFramePrintArea() );
+ aTabRect.Pos() += getFrameArea().Pos();
+ SwRect aTabOutRect( rRect );
+ aTabOutRect.Intersection( aTabRect );
+ SwViewOption::DrawRect( &rRenderContext, aTabOutRect, COL_LIGHTGRAY );
+ }
+ const_cast<SwTabFrame*>(this)->ResetComplete();
+}
+
+/**
+ * Paint border shadow
+ *
+ * @param[in] rRect aligned rect to clip the result
+ * @param[in,out] rOutRect full painting area as input
+ * painting area reduced by shadow space for border and background as output
+ * @param[in] rShadow includes shadow attributes
+ * @param[in] bDrawFullShadowRectangle paint full rect of shadow
+ * @param[in] bTop paint top part of the shadow
+ * @param[in] bBottom paint bottom part of the shadow
+ * @param[in] bLeft paint left part of the shadow
+ * @param[in] bRight paint right part of the shadow
+**/
+static void lcl_PaintShadow( const SwRect& rRect, SwRect& rOutRect,
+ const SvxShadowItem& rShadow, const bool bDrawFullShadowRectangle,
+ const bool bTop, const bool bBottom,
+ const bool bLeft, const bool bRight,
+ SwPaintProperties const & properties)
+{
+ const tools::Long nWidth = ::lcl_AlignWidth ( rShadow.GetWidth(), properties );
+ const tools::Long nHeight = ::lcl_AlignHeight( rShadow.GetWidth(), properties );
+
+ SwRects aRegion;
+ SwRect aOut( rOutRect );
+
+ switch ( rShadow.GetLocation() )
+ {
+ case SvxShadowLocation::BottomRight:
+ {
+ if ( bDrawFullShadowRectangle )
+ {
+ // draw full shadow rectangle
+ aOut.Top( rOutRect.Top() + nHeight );
+ aOut.Left( rOutRect.Left() + nWidth );
+ aRegion.push_back( aOut );
+ }
+ else
+ {
+ if( bBottom )
+ {
+ aOut.Top( rOutRect.Bottom() - nHeight );
+ if( bLeft )
+ aOut.Left( rOutRect.Left() + nWidth );
+ aRegion.push_back( aOut );
+ }
+ if( bRight )
+ {
+ aOut.Left( rOutRect.Right() - nWidth );
+ if( bTop )
+ aOut.Top( rOutRect.Top() + nHeight );
+ else
+ aOut.Top( rOutRect.Top() );
+ if( bBottom )
+ aOut.Bottom( rOutRect.Bottom() - nHeight );
+ aRegion.push_back( aOut );
+ }
+ }
+
+ if( bRight )
+ rOutRect.AddRight(- nWidth );
+ if( bBottom )
+ rOutRect.AddBottom(- nHeight );
+ }
+ break;
+ case SvxShadowLocation::TopLeft:
+ {
+ if ( bDrawFullShadowRectangle )
+ {
+ // draw full shadow rectangle
+ aOut.Bottom( rOutRect.Bottom() - nHeight );
+ aOut.Right( rOutRect.Right() - nWidth );
+ aRegion.push_back( aOut );
+ }
+ else
+ {
+ if( bTop )
+ {
+ aOut.Bottom( rOutRect.Top() + nHeight );
+ if( bRight )
+ aOut.Right( rOutRect.Right() - nWidth );
+ aRegion.push_back( aOut );
+ }
+ if( bLeft )
+ {
+ aOut.Right( rOutRect.Left() + nWidth );
+ if( bBottom )
+ aOut.Bottom( rOutRect.Bottom() - nHeight );
+ else
+ aOut.Bottom( rOutRect.Bottom() );
+ if( bTop )
+ aOut.Top( rOutRect.Top() + nHeight );
+ aRegion.push_back( aOut );
+ }
+ }
+
+ if( bLeft )
+ rOutRect.AddLeft( nWidth );
+ if( bTop )
+ rOutRect.AddTop( nHeight );
+ }
+ break;
+ case SvxShadowLocation::TopRight:
+ {
+ if ( bDrawFullShadowRectangle )
+ {
+ // draw full shadow rectangle
+ aOut.Bottom( rOutRect.Bottom() - nHeight);
+ aOut.Left( rOutRect.Left() + nWidth );
+ aRegion.push_back( aOut );
+ }
+ else
+ {
+ if( bTop )
+ {
+ aOut.Bottom( rOutRect.Top() + nHeight );
+ if( bLeft )
+ aOut.Left( rOutRect.Left() + nWidth );
+ aRegion.push_back( aOut );
+ }
+ if( bRight )
+ {
+ aOut.Left( rOutRect.Right() - nWidth );
+ if( bBottom )
+ aOut.Bottom( rOutRect.Bottom() - nHeight );
+ else
+ aOut.Bottom( rOutRect.Bottom() );
+ if( bTop )
+ aOut.Top( rOutRect.Top() + nHeight );
+ aRegion.push_back( aOut );
+ }
+ }
+
+ if( bRight )
+ rOutRect.AddRight( - nWidth );
+ if( bTop )
+ rOutRect.AddTop( nHeight );
+ }
+ break;
+ case SvxShadowLocation::BottomLeft:
+ {
+ if ( bDrawFullShadowRectangle )
+ {
+ // draw full shadow rectangle
+ aOut.Top( rOutRect.Top() + nHeight );
+ aOut.Right( rOutRect.Right() - nWidth );
+ aRegion.push_back( aOut );
+ }
+ else
+ {
+ if( bBottom )
+ {
+ aOut.Top( rOutRect.Bottom()- nHeight );
+ if( bRight )
+ aOut.Right( rOutRect.Right() - nWidth );
+ aRegion.push_back( aOut );
+ }
+ if( bLeft )
+ {
+ aOut.Right( rOutRect.Left() + nWidth );
+ if( bTop )
+ aOut.Top( rOutRect.Top() + nHeight );
+ else
+ aOut.Top( rOutRect.Top() );
+ if( bBottom )
+ aOut.Bottom( rOutRect.Bottom() - nHeight );
+ aRegion.push_back( aOut );
+ }
+ }
+
+ if( bLeft )
+ rOutRect.AddLeft( nWidth );
+ if( bBottom )
+ rOutRect.AddBottom( - nHeight );
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ vcl::RenderContext *pOut = properties.pSGlobalShell->GetOut();
+
+ DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
+ Color aShadowColor( rShadow.GetColor().GetRGBColor() );
+ if( !aRegion.empty() && properties.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ // In high contrast mode, the output device has already set the
+ // DrawModeFlags::SettingsFill flag. This causes the SetFillColor function
+ // to ignore the setting of a new color. Therefore we have to reset
+ // the drawing mode
+ pOut->SetDrawMode( DrawModeFlags::Default );
+ aShadowColor = SwViewOption::GetFontColor();
+ }
+
+ if ( pOut->GetFillColor() != aShadowColor )
+ pOut->SetFillColor( aShadowColor );
+
+ pOut->SetLineColor();
+
+ pOut->SetDrawMode( nOldDrawMode );
+
+ for (const SwRect & rOut : aRegion)
+ {
+ aOut = rOut;
+ if ( rRect.Overlaps( aOut ) && aOut.Height() > 0 && aOut.Width() > 0 )
+ {
+ aOut.Intersection_( rRect );
+ pOut->DrawRect( aOut.SVRect() );
+ }
+ }
+}
+
+/**
+ * Paints a shadow if the format requests so.
+ *
+ * The shadow is always painted on the outer edge of the OutRect.
+ * If needed, the OutRect is shrunk so the painting of the border can be
+ * done on it.
+ *
+ * @note: draw full shadow rectangle for frames with transparent drawn backgrounds (OD 23.08.2002 #99657#)
+ */
+void SwFrame::PaintShadow( const SwRect& rRect, SwRect& rOutRect,
+ const SwBorderAttrs &rAttrs ) const
+{
+ SvxShadowItem rShadow = rAttrs.GetShadow();
+
+ const bool bCnt = IsContentFrame();
+ const bool bTop = !bCnt || rAttrs.GetTopLine ( *(this) );
+ const bool bBottom = !bCnt || rAttrs.GetBottomLine( *(this) );
+
+ if( IsVertical() )
+ {
+ switch( rShadow.GetLocation() )
+ {
+ case SvxShadowLocation::BottomRight: rShadow.SetLocation(SvxShadowLocation::BottomLeft); break;
+ case SvxShadowLocation::TopLeft: rShadow.SetLocation(SvxShadowLocation::TopRight); break;
+ case SvxShadowLocation::TopRight: rShadow.SetLocation(SvxShadowLocation::BottomRight); break;
+ case SvxShadowLocation::BottomLeft: rShadow.SetLocation(SvxShadowLocation::TopLeft); break;
+ default: break;
+ }
+ }
+
+ // determine, if full shadow rectangle have to be drawn or only two shadow rectangles beside the frame.
+ // draw full shadow rectangle, if frame background is drawn transparent.
+ // Status Quo:
+ // SwLayoutFrame can have transparent drawn backgrounds. Thus,
+ // "asked" their frame format.
+ const bool bDrawFullShadowRectangle =
+ ( IsLayoutFrame() &&
+ static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent()
+ );
+
+ SwRectFnSet aRectFnSet(this);
+ ::lcl_ExtendLeftAndRight( rOutRect, *(this), rAttrs, aRectFnSet.FnRect() );
+
+ lcl_PaintShadow(rRect, rOutRect, rShadow, bDrawFullShadowRectangle, bTop, bBottom, true, true, gProp);
+}
+
+void SwFrame::PaintBorderLine( const SwRect& rRect,
+ const SwRect& rOutRect,
+ const SwPageFrame * pPage,
+ const Color *pColor,
+ const SvxBorderLineStyle nStyle ) const
+{
+ if ( !rOutRect.Overlaps( rRect ) )
+ return;
+
+ SwRect aOut( rOutRect );
+ aOut.Intersection_( rRect );
+
+ const SwTabFrame *pTab = IsCellFrame() ? FindTabFrame() : nullptr;
+ SubColFlags nSubCol = ( IsCellFrame() || IsRowFrame() )
+ ? SubColFlags::Tab
+ : ( IsInSct()
+ ? SubColFlags::Sect
+ : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
+ if( pColor && gProp.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ pColor = &SwViewOption::GetFontColor();
+ }
+
+ if (pPage->GetSortedObjs() &&
+ pPage->GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS))
+ {
+ SwRegionRects aRegion( aOut, 4 );
+ basegfx::utils::B2DClipState aClipState;
+ ::lcl_SubtractFlys( this, pPage, aOut, aRegion, aClipState, gProp );
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ gProp.pSLines->AddLineRect( aRegion[i], pColor, nStyle, pTab, nSubCol, gProp );
+ }
+ else
+ gProp.pSLines->AddLineRect( aOut, pColor, nStyle, pTab, nSubCol, gProp );
+}
+
+namespace drawinglayer::primitive2d
+{
+ namespace {
+
+ class SwBorderRectanglePrimitive2D : public BufferedDecompositionPrimitive2D
+ {
+ private:
+ /// the transformation defining the geometry of this BorderRectangle
+ basegfx::B2DHomMatrix maB2DHomMatrix;
+
+ /// the four styles to be used
+ svx::frame::Style maStyleTop;
+ svx::frame::Style maStyleRight;
+ svx::frame::Style maStyleBottom;
+ svx::frame::Style maStyleLeft;
+
+ protected:
+ /// local decomposition.
+ virtual void create2DDecomposition(
+ Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& rViewInformation) const override;
+
+ public:
+ /// constructor
+ SwBorderRectanglePrimitive2D(
+ const basegfx::B2DHomMatrix& rB2DHomMatrix,
+ const svx::frame::Style& rStyleTop,
+ const svx::frame::Style& rStyleRight,
+ const svx::frame::Style& rStyleBottom,
+ const svx::frame::Style& rStyleLeft);
+
+ /// data read access
+ const basegfx::B2DHomMatrix& getB2DHomMatrix() const { return maB2DHomMatrix; }
+ const svx::frame::Style& getStyleTop() const { return maStyleTop; }
+ const svx::frame::Style& getStyleRight() const { return maStyleRight; }
+ const svx::frame::Style& getStyleBottom() const { return maStyleBottom; }
+ const svx::frame::Style& getStyleLeft() const { return maStyleLeft; }
+
+ /// compare operator
+ virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+
+ /// get range
+ virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
+
+ /// provide unique ID
+ virtual sal_uInt32 getPrimitive2DID() const override;
+ };
+
+ }
+
+ void SwBorderRectanglePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ basegfx::B2DPoint aTopLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 0.0));
+ basegfx::B2DPoint aTopRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 0.0));
+ basegfx::B2DPoint aBottomLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 1.0));
+ basegfx::B2DPoint aBottomRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 1.0));
+
+ // prepare SdrFrameBorderDataVector
+ std::shared_ptr<drawinglayer::primitive2d::SdrFrameBorderDataVector> aData(
+ std::make_shared<drawinglayer::primitive2d::SdrFrameBorderDataVector>());
+
+ if(getStyleTop().IsUsed())
+ {
+ // move top left/right inwards half border width
+ basegfx::B2DVector aDown(getB2DHomMatrix() * basegfx::B2DVector(0.0, 1.0));
+ aDown.setLength(getStyleTop().GetWidth() * 0.5);
+ aTopLeft += aDown;
+ aTopRight += aDown;
+ }
+
+ if(getStyleBottom().IsUsed())
+ {
+ // move bottom left/right inwards half border width
+ basegfx::B2DVector aUp(getB2DHomMatrix() * basegfx::B2DVector(0.0, -1.0));
+ aUp.setLength(getStyleBottom().GetWidth() * 0.5);
+ aBottomLeft += aUp;
+ aBottomRight += aUp;
+ }
+
+ if(getStyleLeft().IsUsed())
+ {
+ // move left top/bottom inwards half border width
+ basegfx::B2DVector aRight(getB2DHomMatrix() * basegfx::B2DVector(1.0, 0.0));
+ aRight.setLength(getStyleLeft().GetWidth() * 0.5);
+ aTopLeft += aRight;
+ aBottomLeft += aRight;
+ }
+
+ if(getStyleRight().IsUsed())
+ {
+ // move right top/bottom inwards half border width
+ basegfx::B2DVector aLeft(getB2DHomMatrix() * basegfx::B2DVector(-1.0, 0.0));
+ aLeft.setLength(getStyleRight().GetWidth() * 0.5);
+ aTopRight += aLeft;
+ aBottomRight += aLeft;
+ }
+
+ // go round-robin, from TopLeft to TopRight, down, left and back up. That
+ // way, the borders will not need to be mirrored in any way
+ if(getStyleTop().IsUsed())
+ {
+ // create BorderPrimitive(s) for top border
+ const basegfx::B2DVector aVector(aTopRight - aTopLeft);
+ aData->emplace_back(
+ aTopLeft,
+ aVector,
+ getStyleTop(),
+ nullptr);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ if(getStyleLeft().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(true, getStyleLeft(), basegfx::B2DVector(aBottomLeft - aTopLeft), false);
+ }
+
+ if(getStyleRight().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(false, getStyleRight(), basegfx::B2DVector(aBottomRight - aTopRight), false);
+ }
+ }
+
+ if(getStyleRight().IsUsed())
+ {
+ // create BorderPrimitive(s) for right border
+ const basegfx::B2DVector aVector(aBottomRight - aTopRight);
+ aData->emplace_back(
+ aTopRight,
+ aVector,
+ getStyleRight(),
+ nullptr);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ if(getStyleTop().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(true, getStyleTop(), basegfx::B2DVector(aTopLeft - aTopRight), false);
+ }
+
+ if(getStyleBottom().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(false, getStyleBottom(), basegfx::B2DVector(aBottomLeft - aBottomRight), false);
+ }
+ }
+
+ if(getStyleBottom().IsUsed())
+ {
+ // create BorderPrimitive(s) for bottom border
+ const basegfx::B2DVector aVector(aBottomLeft - aBottomRight);
+ aData->emplace_back(
+ aBottomRight,
+ aVector,
+ getStyleBottom(),
+ nullptr);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ if(getStyleRight().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(true, getStyleRight(), basegfx::B2DVector(aTopRight - aBottomRight), false);
+ }
+
+ if(getStyleLeft().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(false, getStyleLeft(), basegfx::B2DVector(aTopLeft - aBottomLeft), false);
+ }
+ }
+
+ if(getStyleLeft().IsUsed())
+ {
+ // create BorderPrimitive(s) for left border
+ const basegfx::B2DVector aVector(aTopLeft - aBottomLeft);
+ aData->emplace_back(
+ aBottomLeft,
+ aVector,
+ getStyleLeft(),
+ nullptr);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ if(getStyleBottom().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(true, getStyleBottom(), basegfx::B2DVector(aBottomRight - aBottomLeft), false);
+ }
+
+ if(getStyleTop().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(false, getStyleTop(), basegfx::B2DVector(aTopRight - aTopLeft), false);
+ }
+ }
+
+ // create instance of SdrFrameBorderPrimitive2D if
+ // SdrFrameBorderDataVector is used
+ if(!aData->empty())
+ {
+ rContainer.append(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
+ aData,
+ true))); // force visualization to minimal one discrete unit (pixel)
+ }
+ }
+
+ SwBorderRectanglePrimitive2D::SwBorderRectanglePrimitive2D(
+ const basegfx::B2DHomMatrix& rB2DHomMatrix,
+ const svx::frame::Style& rStyleTop,
+ const svx::frame::Style& rStyleRight,
+ const svx::frame::Style& rStyleBottom,
+ const svx::frame::Style& rStyleLeft)
+ : maB2DHomMatrix(rB2DHomMatrix),
+ maStyleTop(rStyleTop),
+ maStyleRight(rStyleRight),
+ maStyleBottom(rStyleBottom),
+ maStyleLeft(rStyleLeft)
+ {
+ }
+
+ bool SwBorderRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const SwBorderRectanglePrimitive2D& rCompare = static_cast<const SwBorderRectanglePrimitive2D&>(rPrimitive);
+
+ return (getB2DHomMatrix() == rCompare.getB2DHomMatrix() &&
+ getStyleTop() == rCompare.getStyleTop() &&
+ getStyleRight() == rCompare.getStyleRight() &&
+ getStyleBottom() == rCompare.getStyleBottom() &&
+ getStyleLeft() == rCompare.getStyleLeft());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange SwBorderRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+
+ aRetval.transform(getB2DHomMatrix());
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 SwBorderRectanglePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SWBORDERRECTANGLERIMITIVE;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+namespace {
+
+editeng::SvxBorderLine const * get_ptr(std::optional<editeng::SvxBorderLine> const & opt) {
+ return opt ? &*opt : nullptr;
+}
+
+}
+
+void PaintCharacterBorder(
+ const SwFont& rFont,
+ const SwRect& rPaintArea,
+ const bool bVerticalLayout,
+ const bool bVerticalLayoutLRBT,
+ const bool bJoinWithPrev,
+ const bool bJoinWithNext )
+{
+ SwRect aAlignedRect(rPaintArea);
+ SwAlignRect(aAlignedRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
+
+ bool bTop = true;
+ bool bBottom = true;
+ bool bLeft = true;
+ bool bRight = true;
+
+ switch (rFont.GetOrientation(bVerticalLayout, bVerticalLayoutLRBT).get())
+ {
+ case 0 :
+ bLeft = !bJoinWithPrev;
+ bRight = !bJoinWithNext;
+ break;
+ case 900 :
+ bBottom = !bJoinWithPrev;
+ bTop = !bJoinWithNext;
+ break;
+ case 1800 :
+ bRight = !bJoinWithPrev;
+ bLeft = !bJoinWithNext;
+ break;
+ case 2700 :
+ bTop = !bJoinWithPrev;
+ bBottom = !bJoinWithNext;
+ break;
+ }
+
+ // Paint shadow (reduce painting rect)
+ {
+ const SvxShadowItem aShadow(
+ 0, &rFont.GetShadowColor(), rFont.GetShadowWidth(),
+ rFont.GetAbsShadowLocation(bVerticalLayout, bVerticalLayoutLRBT));
+
+ if( aShadow.GetLocation() != SvxShadowLocation::NONE )
+ {
+ lcl_PaintShadow( rPaintArea, aAlignedRect, aShadow,
+ false, bTop, bBottom, bLeft, bRight, gProp);
+ }
+ }
+
+ const basegfx::B2DHomMatrix aBorderTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aAlignedRect.Width(), aAlignedRect.Height(),
+ aAlignedRect.Left(), aAlignedRect.Top()));
+ const svx::frame::Style aStyleTop(
+ bTop ? get_ptr(rFont.GetAbsTopBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
+ 1.0);
+ const svx::frame::Style aStyleRight(
+ bRight ? get_ptr(rFont.GetAbsRightBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
+ 1.0);
+ const svx::frame::Style aStyleBottom(
+ bBottom ? get_ptr(rFont.GetAbsBottomBorder(bVerticalLayout, bVerticalLayoutLRBT))
+ : nullptr,
+ 1.0);
+ const svx::frame::Style aStyleLeft(
+ bLeft ? get_ptr(rFont.GetAbsLeftBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
+ 1.0);
+ drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;
+
+ aBorderLineTarget.append(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
+ aBorderTransform,
+ aStyleTop,
+ aStyleRight,
+ aStyleBottom,
+ aStyleLeft)));
+ gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
+}
+
+/// #i15844#
+static const SwFrame* lcl_HasNextCell( const SwFrame& rFrame )
+{
+ OSL_ENSURE( rFrame.IsCellFrame(),
+ "lcl_HasNextCell( const SwFrame& rFrame ) should be called with SwCellFrame" );
+
+ const SwFrame* pTmpFrame = &rFrame;
+ do
+ {
+ if ( pTmpFrame->GetNext() )
+ return pTmpFrame->GetNext();
+
+ pTmpFrame = pTmpFrame->GetUpper()->GetUpper();
+ }
+ while ( pTmpFrame->IsCellFrame() );
+
+ return nullptr;
+}
+
+/**
+ * Determine cell frame, from which the border attributes
+ * for paint of top/bottom border has to be used.
+ *
+ * OD 21.02.2003 #b4779636#, #107692#
+ *
+ * @param _pCellFrame
+ * input parameter - constant pointer to cell frame for which the cell frame
+ * for the border attributes has to be determined.
+ *
+ * @param _rCellBorderAttrs
+ * input parameter - constant reference to the border attributes of cell frame
+ * <_pCellFrame>.
+ *
+ * @param _bTop
+ * input parameter - boolean, that controls, if cell frame for top border or
+ * for bottom border has to be determined.
+ *
+ * @return constant pointer to cell frame, for which the border attributes has
+ * to be used
+ */
+static const SwFrame* lcl_GetCellFrameForBorderAttrs( const SwFrame* _pCellFrame,
+ const SwBorderAttrs& _rCellBorderAttrs,
+ const bool _bTop )
+{
+ OSL_ENSURE( _pCellFrame, "No cell frame available, dying soon" );
+
+ // determine, if cell frame is at bottom/top border of a table frame and
+ // the table frame has/is a follow.
+ const SwFrame* pTmpFrame = _pCellFrame;
+ bool bCellAtBorder = true;
+ bool bCellAtLeftBorder = !_pCellFrame->GetPrev();
+ bool bCellAtRightBorder = !_pCellFrame->GetNext();
+ while( !pTmpFrame->IsRowFrame() || !pTmpFrame->GetUpper()->IsTabFrame() )
+ {
+ pTmpFrame = pTmpFrame->GetUpper();
+ if ( pTmpFrame->IsRowFrame() &&
+ (_bTop ? pTmpFrame->GetPrev() : pTmpFrame->GetNext())
+ )
+ {
+ bCellAtBorder = false;
+ }
+ if ( pTmpFrame->IsCellFrame() )
+ {
+ if ( pTmpFrame->GetPrev() )
+ {
+ bCellAtLeftBorder = false;
+ }
+ if ( pTmpFrame->GetNext() )
+ {
+ bCellAtRightBorder = false;
+ }
+ }
+ }
+ OSL_ENSURE( pTmpFrame && pTmpFrame->IsRowFrame(), "No RowFrame available" );
+
+ const SwLayoutFrame* pParentRowFrame = static_cast<const SwLayoutFrame*>(pTmpFrame);
+ const SwTabFrame* pParentTabFrame =
+ static_cast<const SwTabFrame*>(pParentRowFrame->GetUpper());
+
+ const bool bCellNeedsAttribute = bCellAtBorder &&
+ ( _bTop ?
+ // bCellInFirstRowWithMaster
+ ( !pParentRowFrame->GetPrev() &&
+ pParentTabFrame->IsFollow() &&
+ 0 == pParentTabFrame->GetTable()->GetRowsToRepeat() ) :
+ // bCellInLastRowWithFollow
+ ( !pParentRowFrame->GetNext() &&
+ pParentTabFrame->GetFollow() )
+ );
+
+ const SwFrame* pRet = _pCellFrame;
+ if ( bCellNeedsAttribute )
+ {
+ // determine, if cell frame has no borders inside the table.
+ const SwFrame* pNextCell = nullptr;
+ bool bNoBordersInside = false;
+
+ if ( bCellAtLeftBorder && ( nullptr != ( pNextCell = lcl_HasNextCell( *_pCellFrame ) ) ) )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNextCell );
+ const SwBorderAttrs &rBorderAttrs = *aAccess.Get();
+ const SvxBoxItem& rBorderBox = rBorderAttrs.GetBox();
+ bCellAtRightBorder = !lcl_HasNextCell( *pNextCell );
+ bNoBordersInside =
+ ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
+ !rBorderBox.GetLeft() &&
+ ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
+ ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
+ }
+ else
+ {
+ const SvxBoxItem& rBorderBox = _rCellBorderAttrs.GetBox();
+ bNoBordersInside =
+ ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
+ ( !rBorderBox.GetLeft() || bCellAtLeftBorder ) &&
+ ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
+ ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
+ }
+
+ if ( bNoBordersInside )
+ {
+ if ( _bTop && !_rCellBorderAttrs.GetBox().GetTop() )
+ {
+ //-hack
+ // Cell frame has no top border and no border inside the table, but
+ // it is at the top border of a table frame, which is a follow.
+ // Thus, use border attributes of cell frame in first row of complete table.
+ // First, determine first table frame of complete table.
+ SwTabFrame* pMasterTabFrame = pParentTabFrame->FindMaster( true );
+ // determine first row of complete table.
+ const SwFrame* pFirstRow = pMasterTabFrame->GetLower();
+ // return first cell in first row
+ SwFrame* pLowerCell = const_cast<SwFrame*>(pFirstRow->GetLower());
+ while ( !pLowerCell->IsCellFrame() ||
+ ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
+ )
+ {
+ pLowerCell = pLowerCell->GetLower();
+ }
+ OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
+ pRet = pLowerCell;
+ }
+ else if ( !_bTop && !_rCellBorderAttrs.GetBox().GetBottom() )
+ {
+ //-hack
+ // Cell frame has no bottom border and no border inside the table,
+ // but it is at the bottom border of a table frame, which has a follow.
+ // Thus, use border attributes of cell frame in last row of complete table.
+ // First, determine last table frame of complete table.
+ SwTabFrame* pLastTabFrame = const_cast<SwTabFrame*>(pParentTabFrame->GetFollow());
+ while ( pLastTabFrame->GetFollow() )
+ {
+ pLastTabFrame = pLastTabFrame->GetFollow();
+ }
+ // determine last row of complete table.
+ SwFrame* pLastRow = pLastTabFrame->GetLastLower();
+ // return first bottom border cell in last row
+ SwFrame* pLowerCell = pLastRow->GetLower();
+ while ( !pLowerCell->IsCellFrame() ||
+ ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
+ )
+ {
+ if ( pLowerCell->IsRowFrame() )
+ {
+ while ( pLowerCell->GetNext() )
+ {
+ pLowerCell = pLowerCell->GetNext();
+ }
+ }
+ pLowerCell = pLowerCell->GetLower();
+ }
+ OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
+ pRet = pLowerCell;
+ }
+ }
+ }
+
+ return pRet;
+}
+
+std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> SwFrame::CreateProcessor2D( ) const
+{
+ basegfx::B2DRange aViewRange;
+
+ SdrPage *pDrawPage = getRootFrame()->GetCurrShell()->Imp()->GetPageView()->GetPage();
+ const drawinglayer::geometry::ViewInformation2D aNewViewInfos(
+ basegfx::B2DHomMatrix( ),
+ getRootFrame()->GetCurrShell()->GetOut()->GetViewTransformation(),
+ aViewRange,
+ GetXDrawPageForSdrPage( pDrawPage ),
+ 0.0);
+
+ return drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(
+ *getRootFrame()->GetCurrShell()->GetOut(),
+ aNewViewInfos );
+}
+
+void SwFrame::ProcessPrimitives( const drawinglayer::primitive2d::Primitive2DContainer& rSequence ) const
+{
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D = CreateProcessor2D();
+ if ( pProcessor2D )
+ {
+ pProcessor2D->process( rSequence );
+ }
+}
+
+/// Paints shadows and borders
+void SwFrame::PaintSwFrameShadowAndBorder(
+ const SwRect& rRect,
+ const SwPageFrame* /*pPage*/,
+ const SwBorderAttrs& rAttrs) const
+{
+ // There's nothing (Row,Body,Footnote,Root,Column,NoText) need to do here
+ if (GetType() & (SwFrameType::NoTxt|SwFrameType::Row|SwFrameType::Body|SwFrameType::Ftn|SwFrameType::Column|SwFrameType::Root))
+ return;
+
+ if (IsCellFrame() && !gProp.pSGlobalShell->GetViewOptions()->IsTable())
+ return;
+
+ // #i29550#
+ if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
+ {
+ const SwTabFrame* pTabFrame = FindTabFrame();
+ if ( pTabFrame->IsCollapsingBorders() )
+ return;
+
+ if ( pTabFrame->GetTable()->IsNewModel() && ( !IsCellFrame() || IsCoveredCell() ) )
+ return;
+ }
+
+ const bool bLine = rAttrs.IsLine();
+ const bool bShadow = rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE;
+
+ // - flag to control,
+ //-hack has to be used.
+ const bool bb4779636HackActive = true;
+
+ const SwFrame* pCellFrameForBottomBorderAttrs = nullptr;
+ const SwFrame* pCellFrameForTopBorderAttrs = nullptr;
+ bool bFoundCellForTopOrBorderAttrs = false;
+ if ( bb4779636HackActive && IsCellFrame() )
+ {
+ pCellFrameForBottomBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, false );
+ if ( pCellFrameForBottomBorderAttrs != this )
+ bFoundCellForTopOrBorderAttrs = true;
+ pCellFrameForTopBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, true );
+ if ( pCellFrameForTopBorderAttrs != this )
+ bFoundCellForTopOrBorderAttrs = true;
+ }
+
+ // - add condition <bFoundCellForTopOrBorderAttrs>
+ //-hack
+ if ( !(bLine || bShadow || bFoundCellForTopOrBorderAttrs) )
+ return;
+
+ //If the rectangle is completely inside the PrtArea, no border needs to
+ //be painted.
+ //For the PrtArea the aligned value needs to be used, otherwise it could
+ //happen, that some parts won't be processed.
+ SwRect aRect( getFramePrintArea() );
+ aRect += getFrameArea().Pos();
+ ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+ // new local boolean variable in order to
+ // suspend border paint under special cases - see below.
+ // NOTE: This is a fix for the implementation of feature #99657#.
+ bool bDrawOnlyShadowForTransparentFrame = false;
+ if ( aRect.Contains( rRect ) )
+ {
+ // paint shadow, if background is transparent.
+ // Because of introduced transparent background for fly frame #99657#,
+ // the shadow have to be drawn if the background is transparent,
+ // in spite the fact that the paint rectangle <rRect> lies fully
+ // in the printing area.
+ // NOTE to chosen solution:
+ // On transparent background, continue processing, but suspend
+ // drawing of border by setting <bDrawOnlyShadowForTransparentFrame>
+ // to true.
+ if ( IsLayoutFrame() &&
+ static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent() )
+ {
+ bDrawOnlyShadowForTransparentFrame = true;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
+ rAttrs.SetGetCacheLine( true );
+
+ if(bShadow)
+ {
+ PaintShadow(rRect, aRect, rAttrs);
+ }
+
+ // suspend drawing of border
+ // add condition < NOT bDrawOnlyShadowForTransparentFrame > - see above
+ // - add condition <bFoundCellForTopOrBorderAttrs>
+ //-hack.
+ if((bLine || bFoundCellForTopOrBorderAttrs) && !bDrawOnlyShadowForTransparentFrame)
+ {
+ // define SvxBorderLine(s) to use
+ const SvxBoxItem& rBox(rAttrs.GetBox());
+ const SvxBorderLine* pLeftBorder(rBox.GetLeft());
+ const SvxBorderLine* pRightBorder(rBox.GetRight());
+ const SvxBorderLine* pTopBorder(rBox.GetTop());
+ const SvxBorderLine* pBottomBorder(rBox.GetBottom());
+
+ // if R2L, exchange Right/Left
+ const bool bR2L(IsCellFrame() && IsRightToLeft());
+
+ if(bR2L)
+ {
+ std::swap(pLeftBorder, pRightBorder);
+ }
+
+ // if ContentFrame and joined Prev/Next, reset top/bottom as needed
+ if(IsContentFrame())
+ {
+ const SwFrame* pDirRefFrame(IsCellFrame() ? FindTabFrame() : this);
+ const SwRectFnSet aRectFnSet(pDirRefFrame);
+ const SwRectFn& _rRectFn(aRectFnSet.FnRect());
+
+ if(rAttrs.JoinedWithPrev(*this))
+ {
+ // tdf#115296 re-add adaptation of vert distance to close the evtl.
+ // existing gap to previous frame
+ const SwFrame* pPrevFrame(GetPrev());
+ (aRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
+
+ // ...and disable top border paint/creation
+ pTopBorder = nullptr;
+ }
+
+ if(rAttrs.JoinedWithNext(*this))
+ {
+ // tdf#115296 re-add adaptation of vert distance to close the evtl.
+ // existing gap to next frame
+ const SwFrame* pNextFrame(GetNext());
+ (aRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
+
+ // ...and disable bottom border paint/creation
+ pBottomBorder = nullptr;
+ }
+ }
+
+ // necessary to replace TopBorder?
+ if((!IsContentFrame() || rAttrs.GetTopLine(*this)) && IsCellFrame() && pCellFrameForTopBorderAttrs != this)
+ {
+ SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForTopBorderAttrs);
+ pTopBorder = aAccess.Get()->GetBox().GetTop();
+ }
+
+ // necessary to replace BottomBorder?
+ if((!IsContentFrame() || rAttrs.GetBottomLine(*this)) && IsCellFrame() && pCellFrameForBottomBorderAttrs != this)
+ {
+ SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForBottomBorderAttrs);
+ pBottomBorder = aAccess.Get()->GetBox().GetBottom();
+ }
+
+ bool bWordBorder = false;
+ SwViewShell* pShell = getRootFrame()->GetCurrShell();
+ if (pShell)
+ {
+ const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
+ bWordBorder = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
+ }
+ bool bInWordTableCell = IsContentFrame() && GetUpper()->IsCellFrame() && bWordBorder;
+ if (bInWordTableCell)
+ {
+ // Compat mode: don't paint bottom border if we know the bottom of the content was cut
+ // off.
+ auto pContentFrame = static_cast<const SwContentFrame*>(this);
+ if (pContentFrame->IsUndersized())
+ {
+ pBottomBorder = nullptr;
+ }
+ }
+
+ if(nullptr != pLeftBorder || nullptr != pRightBorder || nullptr != pTopBorder || nullptr != pBottomBorder)
+ {
+ // now we have all SvxBorderLine(s) sorted out, create geometry
+ const basegfx::B2DHomMatrix aBorderTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aRect.Width(), aRect.Height(),
+ aRect.Left(), aRect.Top()));
+ const svx::frame::Style aStyleTop(pTopBorder, 1.0);
+ svx::frame::Style aStyleRight(pRightBorder, 1.0);
+
+ // Right/bottom page borders are always mirrored in Word.
+ if (IsPageFrame() && bWordBorder)
+ {
+ aStyleRight.MirrorSelf();
+ }
+
+ svx::frame::Style aStyleBottom(pBottomBorder, 1.0);
+
+ if (IsPageFrame() && bWordBorder)
+ {
+ aStyleBottom.MirrorSelf();
+ }
+
+ const svx::frame::Style aStyleLeft(pLeftBorder, 1.0);
+ drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;
+
+ drawinglayer::primitive2d::Primitive2DReference aRetval(
+ new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
+ aBorderTransform,
+ aStyleTop,
+ aStyleRight,
+ aStyleBottom,
+ aStyleLeft));
+
+ if (bInWordTableCell)
+ {
+ // Compat mode: cut off the borders which are outside of our own area.
+ const SwRect& rClip = getFrameArea();
+ basegfx::B2DRectangle aClip(rClip.Left(), rClip.Top(), rClip.Right(),
+ rClip.Bottom());
+ const basegfx::B2DPolyPolygon aPolyPolygon(
+ basegfx::utils::createPolygonFromRect(aClip));
+ const drawinglayer::primitive2d::Primitive2DReference xClipped(
+ new drawinglayer::primitive2d::MaskPrimitive2D(aPolyPolygon, { aRetval }));
+ aRetval = xClipped;
+ }
+
+ aBorderLineTarget.append(aRetval);
+ gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
+ }
+ }
+
+ rAttrs.SetGetCacheLine( false );
+}
+
+/**
+ * Special implementation because of the footnote line
+ *
+ * Currently only the top frame needs to be taken into account
+ * Other lines and shadows are set aside
+ */
+void SwFootnoteContFrame::PaintSwFrameShadowAndBorder(
+ const SwRect& rRect,
+ const SwPageFrame* pPage,
+ const SwBorderAttrs&) const
+{
+ //If the rectangle is completely inside the PrtArea, no border needs to
+ //be painted.
+ SwRect aRect( getFramePrintArea() );
+ aRect.Pos() += getFrameArea().Pos();
+ if ( !aRect.Contains( rRect ) )
+ PaintLine( rRect, pPage );
+}
+
+/// Paint footnote lines.
+void SwFootnoteContFrame::PaintLine( const SwRect& rRect,
+ const SwPageFrame *pPage ) const
+{
+ //The length of the line is derived from the percentual indication on the
+ //PageDesc. The position is also stated on the PageDesc.
+ //The pen can directly be taken from the PageDesc.
+
+ if ( !pPage )
+ pPage = FindPageFrame();
+ const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();
+
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
+ Fraction aFract( nPrtWidth, 1 );
+ aFract *= rInf.GetWidth();
+ const SwTwips nWidth = static_cast<tools::Long>(aFract);
+
+ SwTwips nX = aRectFnSet.GetPrtLeft(*this);
+ switch ( rInf.GetAdj() )
+ {
+ case css::text::HorizontalAdjust_CENTER:
+ nX += nPrtWidth/2 - nWidth/2; break;
+ case css::text::HorizontalAdjust_RIGHT:
+ nX += nPrtWidth - nWidth; break;
+ case css::text::HorizontalAdjust_LEFT:
+ /* do nothing */; break;
+ default:
+ SAL_WARN("sw.core", "New adjustment for footnote lines?");
+ assert(false);
+ }
+ SwTwips nLineWidth = rInf.GetLineWidth();
+ const SwRect aLineRect = aRectFnSet.IsVert() ?
+ SwRect( Point(getFrameArea().Left()+getFrameArea().Width()-rInf.GetTopDist()-nLineWidth,
+ nX), Size( nLineWidth, nWidth ) )
+ : SwRect( Point( nX, getFrameArea().Pos().Y() + rInf.GetTopDist() ),
+ Size( nWidth, rInf.GetLineWidth()));
+ if ( aLineRect.HasArea() && rInf.GetLineStyle() != SvxBorderLineStyle::NONE)
+ PaintBorderLine( rRect, aLineRect , pPage, &rInf.GetLineColor(),
+ rInf.GetLineStyle() );
+}
+
+/// Paints the separator line for inside columns
+void SwLayoutFrame::PaintColLines( const SwRect &rRect, const SwFormatCol &rFormatCol,
+ const SwPageFrame *pPage ) const
+{
+ const SwFrame *pCol = Lower();
+ if ( !pCol || !pCol->IsColumnFrame() )
+ return;
+
+ SwRectFn fnRect = pCol->IsVertical() ? ( pCol->IsVertLR() ? (pCol->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+
+ SwRect aLineRect = getFramePrintArea();
+ aLineRect += getFrameArea().Pos();
+
+ SwTwips nTop = ((aLineRect.*fnRect->fnGetHeight)()*rFormatCol.GetLineHeight())
+ / 100 - (aLineRect.*fnRect->fnGetHeight)();
+ SwTwips nBottom = 0;
+
+ switch ( rFormatCol.GetLineAdj() )
+ {
+ case COLADJ_CENTER:
+ nBottom = nTop / 2; nTop -= nBottom; break;
+ case COLADJ_TOP:
+ nBottom = nTop; nTop = 0; break;
+ case COLADJ_BOTTOM:
+ break;
+ default:
+ OSL_ENSURE( false, "New adjustment for column lines?" );
+ }
+
+ if( nTop )
+ (aLineRect.*fnRect->fnSubTop)( nTop );
+ if( nBottom )
+ (aLineRect.*fnRect->fnAddBottom)( nBottom );
+
+ SwTwips nPenHalf = rFormatCol.GetLineWidth();
+ (aLineRect.*fnRect->fnSetWidth)( nPenHalf );
+ nPenHalf /= 2;
+
+ //We need to be a bit generous here, to not lose something.
+ SwRect aRect( rRect );
+ (aRect.*fnRect->fnSubLeft)( nPenHalf + gProp.nSPixelSzW );
+ (aRect.*fnRect->fnAddRight)( nPenHalf + gProp.nSPixelSzW );
+ SwRectGet fnGetX = IsRightToLeft() ? fnRect->fnGetLeft : fnRect->fnGetRight;
+ while ( pCol->GetNext() )
+ {
+ (aLineRect.*fnRect->fnSetPosX)
+ ( (pCol->getFrameArea().*fnGetX)() - nPenHalf );
+ if ( aRect.Overlaps( aLineRect ) )
+ PaintBorderLine( aRect, aLineRect , pPage, &rFormatCol.GetLineColor(),
+ rFormatCol.GetLineStyle() );
+ pCol = pCol->GetNext();
+ }
+}
+
+void SwPageFrame::PaintGrid( OutputDevice const * pOut, SwRect const &rRect ) const
+{
+ if( !m_bHasGrid || gProp.pSRetoucheFly || gProp.pSRetoucheFly2 )
+ return;
+ SwTextGridItem const*const pGrid(GetGridItem(this));
+ if( !(pGrid && ( OUTDEV_PRINTER != pOut->GetOutDevType() ?
+ pGrid->GetDisplayGrid() : pGrid->GetPrintGrid() )) )
+ return;
+
+ const SwLayoutFrame* pBody = FindBodyCont();
+ if( !pBody )
+ return;
+
+ SwRect aGrid( pBody->getFramePrintArea() );
+ aGrid += pBody->getFrameArea().Pos();
+
+ SwRect aInter( aGrid );
+ aInter.Intersection( rRect );
+ if( !aInter.HasArea() )
+ return;
+
+ bool bGrid = pGrid->GetRubyTextBelow();
+ bool bCell = GRID_LINES_CHARS == pGrid->GetGridType();
+ tools::Long nGrid = pGrid->GetBaseHeight();
+ const SwDoc* pDoc = GetFormat()->GetDoc();
+ tools::Long nGridWidth = GetGridWidth(*pGrid, *pDoc);
+ tools::Long nRuby = pGrid->GetRubyHeight();
+ tools::Long nSum = nGrid + nRuby;
+ const Color *pCol = &pGrid->GetColor();
+
+ SwTwips nRight = aInter.Left() + aInter.Width();
+ SwTwips nBottom = aInter.Top() + aInter.Height();
+ if( IsVertical() )
+ {
+ SwTwips nOrig = aGrid.Left() + aGrid.Width();
+ SwTwips nY = nOrig + nSum *
+ ( ( nOrig - aInter.Left() ) / nSum );
+ SwRect aTmp( Point( nY, aInter.Top() ),
+ Size( 1, aInter.Height() ) );
+ SwTwips nX = aGrid.Top() + nGrid *
+ ( ( aInter.Top() - aGrid.Top() )/ nGrid );
+ if( nX < aInter.Top() )
+ nX += nGrid;
+ SwTwips nGridBottom = aGrid.Top() + aGrid.Height();
+ bool bLeft = aGrid.Top() >= aInter.Top();
+ bool bRight = nGridBottom <= nBottom;
+ bool bBorder = bLeft || bRight;
+ while( nY > nRight )
+ {
+ aTmp.Pos().setX( nY );
+ if( bGrid )
+ {
+ nY -= nGrid;
+ SwTwips nPosY = std::max( SwTwips(aInter.Left()), nY );
+ SwTwips nHeight = std::min(nRight, SwTwips(aTmp.Pos().X()))-nPosY;
+ if( nHeight > 0 )
+ {
+ if( bCell )
+ {
+ SwRect aVert( Point( nPosY, nX ),
+ Size( nHeight, 1 ) );
+ while( aVert.Top() <= nBottom )
+ {
+ PaintBorderLine(rRect,aVert,this,pCol);
+ aVert.Pos().AdjustY(nGrid );
+ }
+ }
+ else if( bBorder )
+ {
+ SwRect aVert( Point( nPosY, aGrid.Top() ),
+ Size( nHeight, 1 ) );
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setY( nGridBottom );
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ else
+ {
+ nY -= nRuby;
+ if( bBorder )
+ {
+ SwTwips nPos = std::max( SwTwips(aInter.Left()), nY );
+ SwTwips nW = std::min(nRight, SwTwips(aTmp.Pos().X())) - nPos;
+ SwRect aVert( Point( nPos, aGrid.Top() ),
+ Size( nW, 1 ) );
+ if( nW > 0 )
+ {
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setY( nGridBottom );
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ bGrid = !bGrid;
+ }
+ while( nY >= aInter.Left() )
+ {
+ aTmp.Pos().setX( nY );
+ PaintBorderLine( rRect, aTmp, this, pCol);
+ if( bGrid )
+ {
+ nY -= nGrid;
+ SwTwips nHeight = aTmp.Pos().X()
+ - std::max(SwTwips(aInter.Left()), nY );
+ if( nHeight > 0 )
+ {
+ if( bCell )
+ {
+ SwRect aVert( Point(aTmp.Pos().X()-nHeight,
+ nX ), Size( nHeight, 1 ) );
+ while( aVert.Top() <= nBottom )
+ {
+ PaintBorderLine(rRect,aVert,this,pCol);
+ aVert.Pos().AdjustY(nGrid );
+ }
+ }
+ else if( bBorder )
+ {
+ SwRect aVert( Point(aTmp.Pos().X()-nHeight,
+ aGrid.Top() ), Size( nHeight, 1 ) );
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setY( nGridBottom );
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ else
+ {
+ nY -= nRuby;
+ if( bBorder )
+ {
+ SwTwips nPos = std::max( SwTwips(aInter.Left()), nY );
+ SwTwips nW = std::min(nRight, SwTwips(aTmp.Pos().X())) - nPos;
+ SwRect aVert( Point( nPos, aGrid.Top() ),
+ Size( nW, 1 ) );
+ if( nW > 0 )
+ {
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setY( nGridBottom );
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ bGrid = !bGrid;
+ }
+ }
+ else
+ {
+ SwTwips nOrig = aGrid.Top();
+ SwTwips nY = nOrig + nSum *( (aInter.Top()-nOrig)/nSum );
+ SwRect aTmp( Point( aInter.Left(), nY ),
+ Size( aInter.Width(), 1 ) );
+ //for textgrid refactor
+ SwTwips nX = aGrid.Left() + nGridWidth *
+ ( ( aInter.Left() - aGrid.Left() )/ nGridWidth );
+ if( nX < aInter.Left() )
+ nX += nGridWidth;
+ SwTwips nGridRight = aGrid.Left() + aGrid.Width();
+ bool bLeft = aGrid.Left() >= aInter.Left();
+ bool bRight = nGridRight <= nRight;
+ bool bBorder = bLeft || bRight;
+ while( nY < aInter.Top() )
+ {
+ aTmp.Pos().setY(nY);
+ if( bGrid )
+ {
+ nY += nGrid;
+ SwTwips nPosY = std::max( aInter.Top(), aTmp.Pos().getY() );
+ SwTwips nHeight = std::min(nBottom, nY ) - nPosY;
+ if( nHeight )
+ {
+ if( bCell )
+ {
+ SwRect aVert( Point( nX, nPosY ),
+ Size( 1, nHeight ) );
+ while( aVert.Left() <= nRight )
+ {
+ PaintBorderLine(rRect,aVert,this,pCol);
+ aVert.Pos().AdjustX(nGridWidth ); //for textgrid refactor
+ }
+ }
+ else if ( bBorder )
+ {
+ SwRect aVert( Point( aGrid.Left(), nPosY ),
+ Size( 1, nHeight ) );
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setX( nGridRight );
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ else
+ {
+ nY += nRuby;
+ if( bBorder )
+ {
+ SwTwips nPos = std::max(aInter.Top(),aTmp.Pos().getY());
+ SwTwips nH = std::min( nBottom, nY ) - nPos;
+ SwRect aVert( Point( aGrid.Left(), nPos ),
+ Size( 1, nH ) );
+ if( nH > 0 )
+ {
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setX(nGridRight);
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ bGrid = !bGrid;
+ }
+ while( nY <= nBottom )
+ {
+ aTmp.Pos().setY(nY);
+ PaintBorderLine( rRect, aTmp, this, pCol);
+ if( bGrid )
+ {
+ nY += nGrid;
+ SwTwips nHeight = std::min(nBottom, nY) - aTmp.Pos().getY();
+ if( nHeight )
+ {
+ if( bCell )
+ {
+ SwRect aVert( Point( nX, aTmp.Pos().getY() ),
+ Size( 1, nHeight ) );
+ while( aVert.Left() <= nRight )
+ {
+ PaintBorderLine( rRect, aVert, this, pCol);
+ aVert.Pos().setX(aVert.Pos().getX() + nGridWidth); //for textgrid refactor
+ }
+ }
+ else if( bBorder )
+ {
+ SwRect aVert( Point( aGrid.Left(),
+ aTmp.Pos().getY() ), Size( 1, nHeight ) );
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setX(nGridRight);
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ else
+ {
+ nY += nRuby;
+ if( bBorder )
+ {
+ SwTwips nPos = std::max(aInter.Top(),aTmp.Pos().Y());
+ SwTwips nH = std::min( nBottom, nY ) - nPos;
+ SwRect aVert( Point( aGrid.Left(), nPos ),
+ Size( 1, nH ) );
+ if( nH > 0 )
+ {
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setX(nGridRight);
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ bGrid = !bGrid;
+ }
+ }
+}
+
+/**
+ * Paint margin area of a page
+ *
+ * OD 20.11.2002 for #104598#:
+ * implement paint of margin area; margin area will be painted for a
+ * view shell with a window and if the document is not in online layout.
+ *
+ * @param _rOutputRect
+ * input parameter - constant instance reference of the rectangle, for
+ * which an output has to be generated.
+ *
+ * @param _pViewShell
+ * input parameter - instance of the view shell, on which the output
+ * has to be generated.
+ */
+void SwPageFrame::PaintMarginArea( const SwRect& _rOutputRect,
+ SwViewShell const * _pViewShell ) const
+{
+ if ( !_pViewShell->GetWin() || _pViewShell->GetViewOptions()->getBrowseMode() )
+ return;
+
+ // Simplified paint with DrawingLayer FillStyle
+ SwRect aPgRect = getFrameArea();
+ aPgRect.Intersection_( _rOutputRect );
+
+ if(!aPgRect.IsEmpty())
+ {
+ OutputDevice *pOut = _pViewShell->GetOut();
+
+ if(pOut->GetFillColor() != aGlobalRetoucheColor)
+ {
+ pOut->SetFillColor(aGlobalRetoucheColor);
+ }
+
+ pOut->DrawRect(aPgRect.SVRect());
+ }
+}
+
+const sal_Int8 SwPageFrame::snShadowPxWidth = 9;
+
+bool SwPageFrame::IsRightShadowNeeded() const
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bIsLTR = getRootFrame()->IsLeftToRightViewLayout();
+
+ // We paint the right shadow if we're not in book mode
+ // or if we've no sibling or are the last page of the "row"
+ return !pSh || (!pSh->GetViewOptions()->IsViewLayoutBookMode()) || !GetNext()
+ || (this == Lower()) || (bIsLTR && OnRightPage())
+ || (!bIsLTR && !OnRightPage());
+
+}
+
+bool SwPageFrame::IsLeftShadowNeeded() const
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bIsLTR = getRootFrame()->IsLeftToRightViewLayout();
+
+ // We paint the left shadow if we're not in book mode
+ // or if we've no sibling or are the last page of the "row"
+ return !pSh || (!pSh->GetViewOptions()->IsViewLayoutBookMode()) || !GetPrev()
+ || (bIsLTR && !OnRightPage())
+ || (!bIsLTR && OnRightPage());
+}
+
+/**
+ * Determine rectangle for bottom page shadow
+ * for #i9719#
+ */
+/*static*/ void SwPageFrame::GetHorizontalShadowRect( const SwRect& _rPageRect,
+ const SwViewShell* _pViewShell,
+ OutputDevice const * pRenderContext,
+ SwRect& _orHorizontalShadowRect,
+ bool bPaintLeftShadow,
+ bool bPaintRightShadow,
+ bool bRightSidebar )
+{
+ const SwPostItMgr *pMgr = _pViewShell->GetPostItMgr();
+ SwRect aAlignedPageRect( _rPageRect );
+ ::SwAlignRect( aAlignedPageRect, _pViewShell, pRenderContext );
+ SwRect aPagePxRect(pRenderContext->LogicToPixel( aAlignedPageRect.SVRect() ));
+
+ tools::Long lShadowAdjustment = snShadowPxWidth - 1; // TODO: extract this
+
+ _orHorizontalShadowRect.Chg(
+ Point( aPagePxRect.Left() + (bPaintLeftShadow ? lShadowAdjustment : 0), 0 ),
+ Size( aPagePxRect.Width() - ( (bPaintLeftShadow ? lShadowAdjustment : 0) + (bPaintRightShadow ? lShadowAdjustment : 0) ),
+ snShadowPxWidth ) );
+
+ if(pMgr && pMgr->ShowNotes() && pMgr->HasNotes())
+ {
+ // Notes are displayed, we've to extend borders
+ SwTwips aSidebarTotalWidth = pMgr->GetSidebarWidth(true) + pMgr->GetSidebarBorderWidth(true);
+ if(bRightSidebar)
+ _orHorizontalShadowRect.AddRight( aSidebarTotalWidth );
+ else
+ _orHorizontalShadowRect.AddLeft( - aSidebarTotalWidth );
+ }
+}
+
+namespace {
+
+enum PaintArea {LEFT, RIGHT, TOP, BOTTOM};
+
+}
+
+#define BORDER_TILE_SIZE 512
+
+/// Wrapper around pOut->DrawBitmapEx.
+static void lcl_paintBitmapExToRect(vcl::RenderContext *pOut, const Point& aPoint, const Size& aSize, const BitmapEx& rBitmapEx, PaintArea eArea)
+{
+ if(!comphelper::LibreOfficeKit::isActive())
+ {
+ // The problem is that if we get called multiple times and the color is
+ // partly transparent, then the result will get darker and darker. To avoid
+ // this, always paint the background color before doing the real paint.
+ tools::Rectangle aRect(aPoint, aSize);
+
+ if (!aRect.IsEmpty())
+ {
+ switch (eArea)
+ {
+ case LEFT: aRect.SetLeft( aRect.Right() - 1 ); break;
+ case RIGHT: aRect.SetRight( aRect.Left() + 1 ); break;
+ case TOP: aRect.SetTop( aRect.Bottom() - 1 ); break;
+ case BOTTOM: aRect.SetBottom( aRect.Top() + 1 ); break;
+ }
+ }
+
+ pOut->SetFillColor(SwViewOption::GetAppBackgroundColor());
+ pOut->SetLineColor();
+ pOut->DrawRect(pOut->PixelToLogic(aRect));
+ }
+
+ // Tiled render if necessary
+ tools::Rectangle aComplete(aPoint, aSize);
+ Size aTileSize(BORDER_TILE_SIZE, BORDER_TILE_SIZE);
+
+ tools::Long iterX = eArea != RIGHT && eArea != LEFT ? BORDER_TILE_SIZE : 0;
+ tools::Long iterY = eArea == RIGHT || eArea == LEFT ? BORDER_TILE_SIZE : 0;
+
+ for (tools::Rectangle aTile(aPoint, aTileSize); true; aTile.Move(iterX, iterY))
+ {
+ tools::Rectangle aRender = aComplete.GetIntersection(aTile);
+ if (aRender.IsEmpty())
+ break;
+ pOut->DrawBitmapEx(pOut->PixelToLogic(aRender.TopLeft()),
+ pOut->PixelToLogic(aRender.GetSize()),
+ Point(0, 0), aRender.GetSize(),
+ rBitmapEx);
+ }
+
+}
+
+/**
+ * Paint page border and shadow
+ *
+ * for #i9719#
+ * implement paint of page border and shadow
+*/
+/*static*/ void SwPageFrame::PaintBorderAndShadow( const SwRect& _rPageRect,
+ const SwViewShell* _pViewShell,
+ bool bPaintLeftShadow,
+ bool bPaintRightShadow,
+ bool bRightSidebar )
+{
+ // No shadow in prefs
+ if (!SwViewOption::IsShadow())
+ return;
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *_pViewShell->GetOut() );
+
+ static vcl::DeleteOnDeinit<drawinglayer::primitive2d::DiscreteShadow> shadowMaskObj(
+ vcl::bitmap::loadFromName(BMP_PAGE_SHADOW_MASK,
+ ImageLoadFlags::IgnoreDarkTheme | ImageLoadFlags::IgnoreScalingFactor));
+
+ drawinglayer::primitive2d::DiscreteShadow& shadowMask = *shadowMaskObj.get();
+ static vcl::DeleteOnDeinit< BitmapEx > aPageTopRightShadowObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageBottomRightShadowObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageBottomLeftShadowObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageBottomShadowBaseObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageRightShadowBaseObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageTopShadowBaseObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageTopLeftShadowObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageLeftShadowBaseObj {};
+ BitmapEx& aPageTopRightShadow = *aPageTopRightShadowObj.get();
+ BitmapEx& aPageBottomRightShadow = *aPageBottomRightShadowObj.get();
+ BitmapEx& aPageBottomLeftShadow = *aPageBottomLeftShadowObj.get();
+ BitmapEx& aPageBottomShadow = *aPageBottomShadowBaseObj.get();
+ BitmapEx& aPageRightShadow = *aPageRightShadowBaseObj.get();
+ BitmapEx& aPageTopShadow = *aPageTopShadowBaseObj.get();
+ BitmapEx& aPageTopLeftShadow = *aPageTopLeftShadowObj.get();
+ BitmapEx& aPageLeftShadow = *aPageLeftShadowBaseObj.get();
+ static Color aShadowColor( COL_AUTO );
+
+ SwRect aAlignedPageRect( _rPageRect );
+ ::SwAlignRect( aAlignedPageRect, _pViewShell, _pViewShell->GetOut() );
+ SwRect aPagePxRect(_pViewShell->GetOut()->LogicToPixel( aAlignedPageRect.SVRect() ));
+
+ if (aShadowColor != SwViewOption::GetShadowColor())
+ {
+ aShadowColor = SwViewOption::GetShadowColor();
+
+ AlphaMask aMask( shadowMask.getBottomRight().GetBitmap() );
+ Bitmap aFilledSquare(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageBottomRightShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getBottomLeft().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageBottomLeftShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getBottom().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageBottomShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getTop().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageTopShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getTopRight().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageTopRightShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getRight().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageRightShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getTopLeft().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageTopLeftShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getLeft().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageLeftShadow = BitmapEx( aFilledSquare, aMask );
+ }
+
+ SwRect aPaintRect;
+ OutputDevice *pOut = _pViewShell->GetOut();
+
+ SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pOut, aPaintRect, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
+
+ // Right shadow & corners
+ if ( bPaintRightShadow )
+ {
+ pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Bottom() + 1 - (aPageBottomRightShadow.GetSizePixel().Height() - snShadowPxWidth) ) ),
+ aPageBottomRightShadow );
+ pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Top() - snShadowPxWidth ) ),
+ aPageTopRightShadow );
+
+ if (aPagePxRect.Height() > 2 * snShadowPxWidth)
+ {
+ const tools::Long nWidth = aPageRightShadow.GetSizePixel().Width();
+ const tools::Long nHeight = aPagePxRect.Height() - 2 * (snShadowPxWidth - 1);
+ if (aPageRightShadow.GetSizePixel().Height() < BORDER_TILE_SIZE)
+ aPageRightShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast);
+
+ lcl_paintBitmapExToRect(pOut,
+ Point(aPaintRect.Right() + snShadowPxWidth, aPagePxRect.Top() + snShadowPxWidth - 1),
+ Size(nWidth, nHeight),
+ aPageRightShadow, RIGHT);
+ }
+ }
+
+ // Left shadows and corners
+ if(bPaintLeftShadow)
+ {
+ const tools::Long lLeft = aPaintRect.Left() - aPageBottomLeftShadow.GetSizePixel().Width();
+ pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft,
+ aPagePxRect.Bottom() + 1 + snShadowPxWidth - aPageBottomLeftShadow.GetSizePixel().Height() ) ), aPageBottomLeftShadow );
+ pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft, aPagePxRect.Top() - snShadowPxWidth ) ), aPageTopLeftShadow );
+ if (aPagePxRect.Height() > 2 * snShadowPxWidth)
+ {
+ const tools::Long nWidth = aPageLeftShadow.GetSizePixel().Width();
+ const tools::Long nHeight = aPagePxRect.Height() - 2 * (snShadowPxWidth - 1);
+ if (aPageLeftShadow.GetSizePixel().Height() < BORDER_TILE_SIZE)
+ aPageLeftShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast);
+
+ lcl_paintBitmapExToRect(pOut,
+ Point(lLeft, aPagePxRect.Top() + snShadowPxWidth - 1),
+ Size(nWidth, nHeight),
+ aPageLeftShadow, LEFT);
+ }
+ }
+
+ // Bottom shadow
+ const tools::Long nBottomHeight = aPageBottomShadow.GetSizePixel().Height();
+ if (aPageBottomShadow.GetSizePixel().Width() < BORDER_TILE_SIZE)
+ aPageBottomShadow.Scale(Size(BORDER_TILE_SIZE, nBottomHeight), BmpScaleFlag::Fast);
+
+ lcl_paintBitmapExToRect(pOut,
+ Point(aPaintRect.Left(), aPagePxRect.Bottom() + 2),
+ Size(aPaintRect.Width(), nBottomHeight),
+ aPageBottomShadow, BOTTOM);
+
+ // Top shadow
+ const tools::Long nTopHeight = aPageTopShadow.GetSizePixel().Height();
+ if (aPageTopShadow.GetSizePixel().Width() < BORDER_TILE_SIZE)
+ aPageTopShadow.Scale(Size(BORDER_TILE_SIZE, nTopHeight), BmpScaleFlag::Fast);
+
+ lcl_paintBitmapExToRect(pOut,
+ Point(aPaintRect.Left(), aPagePxRect.Top() - snShadowPxWidth),
+ Size(aPaintRect.Width(), nTopHeight),
+ aPageTopShadow, TOP);
+}
+
+/**
+ * mod #i6193# paint sidebar for notes
+ * IMPORTANT: if you change the rects here, also change SwPostItMgr::ScrollbarHit
+ */
+/*static*/void SwPageFrame::PaintNotesSidebar(const SwRect& _rPageRect, SwViewShell* _pViewShell, sal_uInt16 nPageNum, bool bRight)
+{
+ //TODO: cut out scrollbar area and arrows out of sidepane rect, otherwise it could flicker when pressing arrow buttons
+ if (!_pViewShell )
+ return;
+
+ SwRect aPageRect( _rPageRect );
+ SwAlignRect( aPageRect, _pViewShell, _pViewShell->GetOut() );
+
+ const SwPostItMgr *pMgr = _pViewShell->GetPostItMgr();
+ if (!(pMgr && pMgr->ShowNotes() && pMgr->HasNotes())) // do not show anything in print preview
+ return;
+
+ sal_Int32 nScrollerHeight = pMgr->GetSidebarScrollerHeight();
+ const tools::Rectangle &aVisRect = _pViewShell->VisArea().SVRect();
+ //draw border and sidepane
+ _pViewShell->GetOut()->SetLineColor();
+ if (!bRight)
+ {
+ _pViewShell->GetOut()->SetFillColor(SwViewOption::GetObjectBoundariesColor());
+ _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarBorderWidth(),aPageRect.Height()))) ;
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ _pViewShell->GetOut()->SetFillColor(COL_BLACK);
+ else
+ _pViewShell->GetOut()->SetFillColor(SwViewOption::GetSectionBoundColor());
+ _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarWidth()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()))) ;
+ }
+ else
+ {
+ _pViewShell->GetOut()->SetFillColor(SwViewOption::GetObjectBoundariesColor());
+ SwRect aSidebarBorder(aPageRect.TopRight(),Size(pMgr->GetSidebarBorderWidth(),aPageRect.Height()));
+ _pViewShell->GetOut()->DrawRect(aSidebarBorder.SVRect());
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ _pViewShell->GetOut()->SetFillColor(COL_BLACK);
+ else
+ _pViewShell->GetOut()->SetFillColor(SwViewOption::GetSectionBoundColor());
+ SwRect aSidebar(Point(aPageRect.Right()+pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()));
+ _pViewShell->GetOut()->DrawRect(aSidebar.SVRect());
+ }
+ if (!pMgr->ShowScrollbar(nPageNum))
+ return;
+
+ // draw scrollbar area and arrows
+ Point aPointBottom;
+ Point aPointTop;
+ aPointBottom = !bRight ? Point(aPageRect.Left() - pMgr->GetSidebarWidth() - pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- _pViewShell->GetOut()->PixelToLogic(Size(0,2+pMgr->GetSidebarScrollerHeight())).Height()) :
+ Point(aPageRect.Right() + pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- _pViewShell->GetOut()->PixelToLogic(Size(0,2+pMgr->GetSidebarScrollerHeight())).Height());
+ aPointTop = !bRight ? Point(aPageRect.Left() - pMgr->GetSidebarWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + _pViewShell->GetOut()->PixelToLogic(Size(0,2)).Height()) :
+ Point(aPageRect.Right() + pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + _pViewShell->GetOut()->PixelToLogic(Size(0,2)).Height());
+ Size aSize(pMgr->GetSidebarWidth() - _pViewShell->GetOut()->PixelToLogic(Size(4,0)).Width(), _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()) ;
+ tools::Rectangle aRectBottom(aPointBottom,aSize);
+ tools::Rectangle aRectTop(aPointTop,aSize);
+
+ if (aRectBottom.Overlaps(aVisRect))
+ {
+
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ _pViewShell->GetOut()->SetLineColor(COL_WHITE);
+ _pViewShell->GetOut()->SetFillColor(COL_BLACK);
+ }
+ else
+ {
+ _pViewShell->GetOut()->SetLineColor(COL_BLACK);
+ _pViewShell->GetOut()->SetFillColor(COL_LIGHTGRAY);
+ }
+ _pViewShell->GetOut()->DrawRect(aRectBottom);
+ _pViewShell->GetOut()->DrawLine(aPointBottom + Point(pMgr->GetSidebarWidth()/3,0), aPointBottom + Point(pMgr->GetSidebarWidth()/3 , _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()));
+
+ _pViewShell->GetOut()->SetLineColor();
+ Point aMiddleFirst(aPointBottom + Point(pMgr->GetSidebarWidth()/6,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
+ Point aMiddleSecond(aPointBottom + Point(pMgr->GetSidebarWidth()/3*2,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
+ PaintNotesSidebarArrows(aMiddleFirst,aMiddleSecond,_pViewShell,pMgr->GetArrowColor(KEY_PAGEUP,nPageNum), pMgr->GetArrowColor(KEY_PAGEDOWN,nPageNum));
+ }
+ if (!aRectTop.Overlaps(aVisRect))
+ return;
+
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ _pViewShell->GetOut()->SetLineColor(COL_WHITE);
+ _pViewShell->GetOut()->SetFillColor(COL_BLACK);
+ }
+ else
+ {
+ _pViewShell->GetOut()->SetLineColor(COL_BLACK);
+ _pViewShell->GetOut()->SetFillColor(COL_LIGHTGRAY);
+ }
+ _pViewShell->GetOut()->DrawRect(aRectTop);
+ _pViewShell->GetOut()->DrawLine(aPointTop + Point(pMgr->GetSidebarWidth()/3*2,0), aPointTop + Point(pMgr->GetSidebarWidth()/3*2 , _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()));
+
+ _pViewShell->GetOut()->SetLineColor();
+ Point aMiddleFirst(aPointTop + Point(pMgr->GetSidebarWidth()/3,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
+ Point aMiddleSecond(aPointTop + Point(pMgr->GetSidebarWidth()/6*5,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
+ PaintNotesSidebarArrows(aMiddleFirst,aMiddleSecond,_pViewShell, pMgr->GetArrowColor(KEY_PAGEUP,nPageNum), pMgr->GetArrowColor(KEY_PAGEDOWN,nPageNum));
+}
+
+/*static*/ void SwPageFrame::PaintNotesSidebarArrows(const Point &aMiddleFirst, const Point &aMiddleSecond, SwViewShell const * _pViewShell, const Color& rColorUp, const Color& rColorDown)
+{
+ tools::Polygon aTriangleUp(3);
+ tools::Polygon aTriangleDown(3);
+
+ aTriangleUp.SetPoint(aMiddleFirst + Point(0,_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),0);
+ aTriangleUp.SetPoint(aMiddleFirst + Point(_pViewShell->GetOut()->PixelToLogic(Size(-3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),1);
+ aTriangleUp.SetPoint(aMiddleFirst + Point(_pViewShell->GetOut()->PixelToLogic(Size(3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),2);
+
+ aTriangleDown.SetPoint(aMiddleSecond + Point(_pViewShell->GetOut()->PixelToLogic(Size(-3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),0);
+ aTriangleDown.SetPoint(aMiddleSecond + Point(_pViewShell->GetOut()->PixelToLogic(Size(+3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),1);
+ aTriangleDown.SetPoint(aMiddleSecond + Point(0,_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),2);
+
+ _pViewShell->GetOut()->SetFillColor(rColorUp);
+ _pViewShell->GetOut()->DrawPolygon(aTriangleUp);
+ _pViewShell->GetOut()->SetFillColor(rColorDown);
+ _pViewShell->GetOut()->DrawPolygon(aTriangleDown);
+}
+
+/**
+ * Get bound rectangle of border and shadow for repaints
+ *
+ * for #i9719#
+ */
+/*static*/ void SwPageFrame::GetBorderAndShadowBoundRect( const SwRect& _rPageRect,
+ const SwViewShell* _pViewShell,
+ OutputDevice const * pRenderContext,
+ SwRect& _orBorderAndShadowBoundRect,
+ bool bLeftShadow,
+ bool bRightShadow,
+ bool bRightSidebar
+ )
+{
+ SwRect aAlignedPageRect( _rPageRect );
+ ::SwAlignRect( aAlignedPageRect, _pViewShell, pRenderContext );
+ SwRect aPagePxRect(pRenderContext->LogicToPixel( aAlignedPageRect.SVRect() ));
+ aPagePxRect.AddBottom( snShadowPxWidth + 1 );
+ aPagePxRect.AddTop( - snShadowPxWidth - 1 );
+
+ SwRect aTmpRect;
+
+ // Always ask for full shadow since we want a bounding rect
+ // including at least the page frame
+ SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pRenderContext, aTmpRect, false, false, bRightSidebar );
+
+ if(bLeftShadow) aPagePxRect.Left( aTmpRect.Left() - snShadowPxWidth - 1);
+ if(bRightShadow) aPagePxRect.Right( aTmpRect.Right() + snShadowPxWidth + 1);
+
+ _orBorderAndShadowBoundRect = SwRect(pRenderContext->PixelToLogic( aPagePxRect.SVRect() ));
+}
+
+SwRect SwPageFrame::GetBoundRect(OutputDevice const * pOutputDevice) const
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ SwRect aPageRect( getFrameArea() );
+ SwRect aResult;
+
+ if(!pSh) {
+ return SwRect( Point(0, 0), Size(0, 0) );
+ }
+
+ SwPageFrame::GetBorderAndShadowBoundRect( aPageRect, pSh, pOutputDevice, aResult,
+ IsLeftShadowNeeded(), IsRightShadowNeeded(), SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT );
+ return aResult;
+}
+
+/*static*/ SwTwips SwPageFrame::GetSidebarBorderWidth( const SwViewShell* _pViewShell )
+{
+ const SwPostItMgr* pPostItMgr = _pViewShell ? _pViewShell->GetPostItMgr() : nullptr;
+ const SwTwips nRet = pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ? pPostItMgr->GetSidebarWidth() + pPostItMgr->GetSidebarBorderWidth() : 0;
+ return nRet;
+}
+
+void SwFrame::PaintBaBo( const SwRect& rRect, const SwPageFrame *pPage,
+ const bool bOnlyTextBackground ) const
+{
+ if ( !pPage )
+ pPage = FindPageFrame();
+
+ OutputDevice *pOut = gProp.pSGlobalShell->GetOut();
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
+
+ pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ pOut->SetLineColor();
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ // take care of page margin area
+ // Note: code move from <SwFrame::PaintSwFrameBackground(..)> to new method
+ // <SwPageFrame::Paintmargin(..)>.
+ if ( IsPageFrame() && !bOnlyTextBackground)
+ {
+ static_cast<const SwPageFrame*>(this)->PaintMarginArea( rRect, gProp.pSGlobalShell );
+ }
+
+ // paint background
+ {
+ PaintSwFrameBackground( rRect, pPage, rAttrs, false, true/*bLowerBorder*/, bOnlyTextBackground );
+ }
+
+ // paint border before painting background
+ // paint grid for page frame and paint border
+ if (!bOnlyTextBackground)
+ {
+ SwRect aRect( rRect );
+
+ if( IsPageFrame() )
+ {
+ static_cast<const SwPageFrame*>(this)->PaintGrid( pOut, aRect );
+ }
+
+ PaintSwFrameShadowAndBorder(aRect, pPage, rAttrs);
+ }
+
+ pOut->Pop();
+}
+
+static bool lcl_compareFillAttributes(const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pA, const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pB)
+{
+ if (pA == pB)
+ return true;
+ if (!pA || !pB)
+ return false;
+ return pA->getFillAttribute() == pB->getFillAttribute();
+}
+
+/// Do not paint background for fly frames without a background brush by
+/// calling <PaintBaBo> at the page or at the fly frame its anchored
+void SwFrame::PaintSwFrameBackground( const SwRect &rRect, const SwPageFrame *pPage,
+ const SwBorderAttrs & rAttrs,
+ const bool bLowerMode,
+ const bool bLowerBorder,
+ const bool bOnlyTextBackground ) const
+{
+ // #i1837# - no paint of table background, if corresponding option is *not* set.
+ if( IsTabFrame() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsTable() )
+ {
+ return;
+ }
+
+ // nothing to do for covered table cells:
+ if( IsCellFrame() && IsCoveredCell() )
+ return;
+
+ SwViewShell *pSh = gProp.pSGlobalShell;
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );
+
+ const SvxBrushItem* pItem;
+ // temporary background brush for a fly frame without a background brush
+ std::unique_ptr<SvxBrushItem> pTmpBackBrush;
+ std::optional<Color> pCol;
+ SwRect aOrigBackRect;
+ const bool bPageFrame = IsPageFrame();
+ bool bLowMode = true;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ bool bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, bLowerMode, /*bConsiderTextBox=*/false );
+
+ // show track changes of table row
+ if( IsRowFrame() && !getRootFrame()->IsHideRedlines() )
+ {
+ RedlineType eType = static_cast<const SwRowFrame*>(this)->GetTabLine()->GetRedlineType();
+ if ( RedlineType::Delete == eType || RedlineType::Insert == eType )
+ {
+ pCol = RedlineType::Delete == eType ? COL_AUTHOR_TABLE_DEL : COL_AUTHOR_TABLE_INS;
+ bBack = true;
+ }
+ }
+ else if ( bBack && IsCellFrame() && !getRootFrame()->IsHideRedlines() &&
+ // skip cell background to show the row colored according to its tracked change
+ RedlineType::None != static_cast<const SwRowFrame*>(GetUpper())->GetTabLine()->GetRedlineType() )
+ {
+ return;
+ }
+
+ //- Output if a separate background is used.
+ bool bNoFlyBackground = !gProp.bSFlyMetafile && !bBack && IsFlyFrame();
+ if ( bNoFlyBackground )
+ {
+ // Fly frame has no background.
+ // Try to find background brush at parents, if previous call of
+ // <GetBackgroundBrush> disabled this option with the parameter <bLowerMode>
+ if ( bLowerMode )
+ {
+ bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/false );
+ }
+ // If still no background found for the fly frame, initialize the
+ // background brush <pItem> with global retouche color and set <bBack>
+ // to true, that fly frame will paint its background using this color.
+ if ( !bBack )
+ {
+ // #i6467# - on print output, pdf output and in embedded mode not editing color COL_WHITE is used
+ // instead of the global retouche color.
+ if ( pSh->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
+ pSh->GetViewOptions()->IsPDFExport() ||
+ ( pSh->GetDoc()->GetDocShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED &&
+ !pSh->GetDoc()->GetDocShell()->IsInPlaceActive()
+ )
+ )
+ {
+ pTmpBackBrush.reset(new SvxBrushItem( COL_WHITE, RES_BACKGROUND ));
+
+ //UUU
+ aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(COL_WHITE);
+ }
+ else
+ {
+ pTmpBackBrush.reset(new SvxBrushItem( aGlobalRetoucheColor, RES_BACKGROUND));
+
+ //UUU
+ aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aGlobalRetoucheColor);
+ }
+
+ pItem = pTmpBackBrush.get();
+ bBack = true;
+ }
+ }
+
+ SwRect aPaintRect( getFrameArea() );
+ if( IsTextFrame() || IsSctFrame() )
+ aPaintRect = UnionFrame( true );
+
+ // bOnlyTextBackground means background that's on top of background shapes,
+ // this includes both text and cell frames.
+ if ( (!bOnlyTextBackground || IsTextFrame() || IsCellFrame()) && aPaintRect.Overlaps( rRect ) )
+ {
+ if ( bBack || bPageFrame || !bLowerMode )
+ {
+ const bool bBrowse = pSh->GetViewOptions()->getBrowseMode();
+ SwRect aRect;
+ if ( (bPageFrame && bBrowse) ||
+ (IsTextFrame() && getFramePrintArea().SSize() == getFrameArea().SSize()) )
+ {
+ aRect = getFrameArea();
+ ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+ }
+ else
+ {
+ if (bPageFrame && GetAttrSet()->GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue())
+ {
+ aRect = getFrameArea();
+ ::SwAlignRect(aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
+ }
+ else
+ {
+ ::lcl_CalcBorderRect( aRect, this, rAttrs, false, gProp);
+ }
+
+ if ( (IsTextFrame() || IsTabFrame()) && GetPrev() )
+ {
+ if ( GetPrev()->GetAttrSet()->GetBackground() == GetAttrSet()->GetBackground() &&
+ lcl_compareFillAttributes(GetPrev()->getSdrAllFillAttributesHelper(), getSdrAllFillAttributesHelper()))
+ {
+ aRect.Top( getFrameArea().Top() );
+ }
+ }
+ }
+ aRect.Intersection( rRect );
+
+ OutputDevice *pOut = pSh->GetOut();
+
+ if ( aRect.HasArea() )
+ {
+ std::unique_ptr<SvxBrushItem> pNewItem;
+
+ if( pCol )
+ {
+ pNewItem.reset(new SvxBrushItem( *pCol, RES_BACKGROUND ));
+ pItem = pNewItem.get();
+ aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(*pCol);
+ }
+
+ SwRegionRects aRegion( aRect );
+ basegfx::B2DPolygon aB2DPolygon{tools::Polygon(aRect.SVRect()).getB2DPolygon()};
+ basegfx::utils::B2DClipState aClipState{basegfx::B2DPolyPolygon(aB2DPolygon)};
+ if (pPage->GetSortedObjs() &&
+ pSh->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS))
+ {
+ ::lcl_SubtractFlys( this, pPage, aRect, aRegion, aClipState, gProp );
+ }
+
+ // Determine, if background transparency
+ // have to be considered for drawing.
+ // Status Quo: background transparency have to be
+ // considered for fly frames
+ const bool bConsiderBackgroundTransparency = IsFlyFrame();
+ bool bDone(false);
+
+ // #i125189# We are also done when the new DrawingLayer FillAttributes are used
+ // or the FillStyle is set (different from drawing::FillStyle_NONE)
+ if (aFillAttributes)
+ {
+ if(aFillAttributes->isUsed())
+ {
+ // check if really something is painted
+ bDone = DrawFillAttributes(aFillAttributes, aOrigBackRect, aRegion, aClipState, *pOut);
+ }
+
+ if(!bDone)
+ {
+ // if not, still a FillStyle could be set but the transparency is at 100%,
+ // thus need to check the model data itself for FillStyle (do not rely on
+ // SdrAllFillAttributesHelper since it already contains optimized information,
+ // e.g. transparency leads to no fill)
+ const drawing::FillStyle eFillStyle(GetAttrSet()->Get(XATTR_FILLSTYLE).GetValue());
+
+ if(drawing::FillStyle_NONE != eFillStyle)
+ {
+ bDone = true;
+ }
+ }
+ }
+
+ if(!bDone)
+ {
+ for (size_t i = 0; i < aRegion.size(); ++i)
+ {
+ if (1 < aRegion.size())
+ {
+ ::SwAlignRect( aRegion[i], gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+ if( !aRegion[i].HasArea() )
+ continue;
+ }
+ // add 6th parameter to indicate, if background transparency have to be considered
+ // Set missing 5th parameter to the default value GRFNUM_NO
+ // - see declaration in /core/inc/frmtool.hxx.
+ ::DrawGraphic(
+ pItem,
+ *pOut,
+ aOrigBackRect,
+ aRegion[i],
+ GRFNUM_NO,
+ bConsiderBackgroundTransparency );
+ }
+ }
+ }
+ }
+ else
+ bLowMode = bLowerMode;
+ }
+
+ // delete temporary background brush.
+ pTmpBackBrush.reset();
+
+ //Now process lower and his neighbour.
+ //We end this as soon as a Frame leaves the chain and therefore is not a lower
+ //of me anymore
+ const SwFrame *pFrame = GetLower();
+ if ( !pFrame )
+ return;
+
+ SwRect aFrameRect;
+ SwRect aRect( GetPaintArea() );
+ aRect.Intersection_( rRect );
+ SwRect aBorderRect( aRect );
+ SwShortCut aShortCut( *pFrame, aBorderRect );
+ do
+ { if ( gProp.pSProgress )
+ SfxProgress::Reschedule();
+
+ aFrameRect = pFrame->GetPaintArea();
+ if ( aFrameRect.Overlaps( aBorderRect ) )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
+ const SwBorderAttrs &rTmpAttrs = *aAccess.Get();
+ if ( ( pFrame->IsLayoutFrame() && bLowerBorder ) || aFrameRect.Overlaps( aRect ) )
+ {
+ pFrame->PaintSwFrameBackground( aRect, pPage, rTmpAttrs, bLowMode,
+ bLowerBorder, bOnlyTextBackground );
+ }
+
+ if ( bLowerBorder )
+ {
+ pFrame->PaintSwFrameShadowAndBorder( aBorderRect, pPage, rTmpAttrs );
+ }
+ }
+ pFrame = pFrame->GetNext();
+ } while ( pFrame && pFrame->GetUpper() == this &&
+ !aShortCut.Stop( aFrameRect ) );
+}
+
+/// Refreshes all subsidiary lines of a page.
+void SwPageFrame::RefreshSubsidiary( const SwRect &rRect ) const
+{
+ if ( !(isSubsidiaryLinesEnabled() || isTableBoundariesEnabled()
+ || isSubsidiaryLinesForSectionsEnabled() || isSubsidiaryLinesFlysEnabled()) )
+ return;
+
+ if ( !rRect.HasArea() )
+ return;
+
+ //During paint using the root, the array is controlled from there.
+ //Otherwise we'll handle it for our self.
+ bool bDelSubs = false;
+ if ( !gProp.pSSubsLines )
+ {
+ gProp.pSSubsLines.reset(new SwSubsRects);
+ // create container for special subsidiary lines
+ gProp.pSSpecSubsLines.reset(new SwSubsRects);
+ bDelSubs = true;
+ }
+
+ RefreshLaySubsidiary( this, rRect );
+
+ if ( bDelSubs )
+ {
+ // paint special subsidiary lines and delete its container
+ gProp.pSSpecSubsLines->PaintSubsidiary( gProp.pSGlobalShell->GetOut(), nullptr, gProp );
+ gProp.pSSpecSubsLines.reset();
+
+ gProp.pSSubsLines->PaintSubsidiary(gProp.pSGlobalShell->GetOut(), gProp.pSLines.get(), gProp);
+ gProp.pSSubsLines.reset();
+ }
+}
+
+void SwLayoutFrame::RefreshLaySubsidiary( const SwPageFrame *pPage,
+ const SwRect &rRect ) const
+{
+ const bool bSubsOpt = isSubsidiaryLinesEnabled();
+ if ( bSubsOpt )
+ PaintSubsidiaryLines( pPage, rRect );
+
+ const SwFrame *pLow = Lower();
+ if( !pLow )
+ return;
+ SwShortCut aShortCut( *pLow, rRect );
+ while( pLow && !aShortCut.Stop( pLow->getFrameArea() ) )
+ {
+ if ( pLow->getFrameArea().Overlaps( rRect ) && pLow->getFrameArea().HasArea() )
+ {
+ if ( pLow->IsLayoutFrame() )
+ static_cast<const SwLayoutFrame*>(pLow)->RefreshLaySubsidiary( pPage, rRect);
+ else if ( pLow->GetDrawObjs() )
+ {
+ const SwSortedObjs& rObjs = *(pLow->GetDrawObjs());
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if ( pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
+ pAnchoredObj->GetDrawObj()->GetLayer() ) )
+ if (auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() && pFly->getFrameArea().Overlaps( rRect ) )
+ {
+ if ( !pFly->Lower() || !pFly->Lower()->IsNoTextFrame() ||
+ !static_cast<const SwNoTextFrame*>(pFly->Lower())->HasAnimation())
+ pFly->RefreshLaySubsidiary( pPage, rRect );
+ }
+ }
+ }
+ }
+ }
+ pLow = pLow->GetNext();
+ }
+}
+
+/**
+ * Subsidiary lines to paint the PrtAreas
+ * Only the LayoutFrames which directly contain Content
+ * Paints the desired line and pays attention to not overpaint any flys
+ */
+static void lcl_RefreshLine( const SwLayoutFrame *pLay,
+ const SwPageFrame *pPage,
+ const Point &rP1,
+ const Point &rP2,
+ const SubColFlags nSubColor,
+ SwLineRects* pSubsLines )
+{
+ //In which direction do we loop? Can only be horizontal or vertical.
+ OSL_ENSURE( ((rP1.X() == rP2.X()) || (rP1.Y() == rP2.Y())),
+ "Sloped subsidiary lines are not allowed." );
+
+ const bool bHori = rP1.Y() == rP2.Y();
+
+ // use pointers to member function in order to unify flow
+ typedef tools::Long (Point::*pmfPtGet)() const;
+ typedef void (Point::*pmfPtSet)(tools::Long);
+ const pmfPtGet pDirPtX = &Point::X;
+ const pmfPtGet pDirPtY = &Point::Y;
+ const pmfPtGet pDirPt = bHori ? pDirPtX : pDirPtY;
+ const pmfPtSet pDirPtSetX = &Point::setX;
+ const pmfPtSet pDirPtSetY = &Point::setY;
+ const pmfPtSet pDirPtSet = bHori ? pDirPtSetX : pDirPtSetY;
+
+ Point aP1( rP1 );
+ Point aP2( rP2 );
+
+ while ( (aP1.*pDirPt)() < (aP2.*pDirPt)() )
+ {
+ //If the starting point lies in a fly, it is directly set behind the
+ //fly.
+ //The end point moves to the start if the end point lies in a fly or we
+ //have a fly between starting point and end point.
+ // In this way, every position is output one by one.
+
+ //If I'm a fly I'll only avoid those flys which are places 'above' me;
+ //this means those who are behind me in the array.
+ //Even if I'm inside a fly or inside a fly inside a fly a.s.o I won't
+ //avoid any of those flys.
+ SwOrderIter aIter( pPage );
+ const SwFlyFrame *pMyFly = pLay->FindFlyFrame();
+ if ( pMyFly )
+ {
+ aIter.Current( pMyFly->GetVirtDrawObj() );
+ while ( nullptr != (pMyFly = pMyFly->GetAnchorFrame()->FindFlyFrame()) )
+ {
+ if ( aIter()->GetOrdNum() > pMyFly->GetVirtDrawObj()->GetOrdNum() )
+ aIter.Current( pMyFly->GetVirtDrawObj() );
+ }
+ }
+ else
+ aIter.Bottom();
+
+ while ( aIter() )
+ {
+ const SwVirtFlyDrawObj *pObj = static_cast<const SwVirtFlyDrawObj*>(aIter());
+ const SwFlyFrame *pFly = pObj ? pObj->GetFlyFrame() : nullptr;
+
+ //I certainly won't avoid myself, even if I'm placed _inside_ the
+ //fly I won't avoid it.
+ if ( !pFly || (pFly == pLay || pFly->IsAnLower( pLay )) )
+ {
+ aIter.Next();
+ continue;
+ }
+
+ // do *not* consider fly frames with a transparent background.
+ // do *not* consider fly frame, which belongs to an invisible layer
+ if ( pFly->IsBackgroundTransparent() ||
+ !pFly->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( pObj->GetLayer() ) )
+ {
+ aIter.Next();
+ continue;
+ }
+
+ //Is the Obj placed on the line
+ const tools::Long nP1OthPt = !bHori ? rP1.X() : rP1.Y();
+ const tools::Rectangle &rBound = pObj->GetCurrentBoundRect();
+ const Point aDrPt( rBound.TopLeft() );
+ const tools::Long nDrOthPt = !bHori ? aDrPt.X() : aDrPt.Y();
+ const Size aDrSz( rBound.GetSize() );
+ const tools::Long nDrOthSz = !bHori ? aDrSz.Width() : aDrSz.Height();
+
+ if ( nP1OthPt >= nDrOthPt && nP1OthPt <= nDrOthPt + nDrOthSz )
+ {
+ const tools::Long nDrDirPt = bHori ? aDrPt.X() : aDrPt.Y();
+ const tools::Long nDrDirSz = bHori ? aDrSz.Width() : aDrSz.Height();
+
+ if ( (aP1.*pDirPt)() >= nDrDirPt && (aP1.*pDirPt)() <= nDrDirPt + nDrDirSz )
+ (aP1.*pDirPtSet)( nDrDirPt + nDrDirSz );
+
+ if ( (aP2.*pDirPt)() >= nDrDirPt && (aP1.*pDirPt)() < (nDrDirPt - 1) )
+ (aP2.*pDirPtSet)( nDrDirPt - 1 );
+ }
+ aIter.Next();
+ }
+
+ if ( (aP1.*pDirPt)() < (aP2.*pDirPt)() )
+ {
+ SwRect aRect( aP1, aP2 );
+ // use parameter <pSubsLines> instead of global variable <gProp.pSSubsLines>.
+ pSubsLines->AddLineRect( aRect, nullptr, SvxBorderLineStyle::SOLID,
+ nullptr, nSubColor, gProp );
+ }
+ aP1 = aP2;
+ (aP1.*pDirPtSet)( (aP1.*pDirPt)() + 1 );
+ aP2 = rP2;
+ }
+}
+
+static drawinglayer::primitive2d::Primitive2DContainer lcl_CreatePageAreaDelimiterPrimitives(
+ const SwRect& rRect )
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSeq( 4 );
+
+ basegfx::BColor aLineColor = SwViewOption::GetDocBoundariesColor().getBColor();
+ double nLineLength = 200.0; // in Twips
+
+ Point aPoints[] = { rRect.TopLeft(), rRect.TopRight(), rRect.BottomRight(), rRect.BottomLeft() };
+ double const aXOffDirs[] = { -1.0, 1.0, 1.0, -1.0 };
+ double const aYOffDirs[] = { -1.0, -1.0, 1.0, 1.0 };
+
+ // Actually loop over the corners to create the two lines
+ for ( int i = 0; i < 4; i++ )
+ {
+ basegfx::B2DVector aHorizVector( aXOffDirs[i], 0.0 );
+ basegfx::B2DVector aVertVector( 0.0, aYOffDirs[i] );
+
+ basegfx::B2DPoint aBPoint( aPoints[i].getX(), aPoints[i].getY() );
+
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append( aBPoint + aHorizVector * nLineLength );
+ aPolygon.append( aBPoint );
+ aPolygon.append( aBPoint + aVertVector * nLineLength );
+
+ aSeq[i] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aPolygon, aLineColor );
+ }
+
+ return aSeq;
+}
+
+static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateRectangleDelimiterPrimitives (
+ const SwRect& rRect )
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSeq( 1 );
+ basegfx::BColor aLineColor = SwViewOption::GetDocBoundariesColor().getBColor();
+
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append( basegfx::B2DPoint( rRect.Left(), rRect.Top() ) );
+ aPolygon.append( basegfx::B2DPoint( rRect.Right(), rRect.Top() ) );
+ aPolygon.append( basegfx::B2DPoint( rRect.Right(), rRect.Bottom() ) );
+ aPolygon.append( basegfx::B2DPoint( rRect.Left(), rRect.Bottom() ) );
+ aPolygon.setClosed( true );
+
+ aSeq[0] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aPolygon, aLineColor );
+
+ return aSeq;
+}
+
+static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateColumnAreaDelimiterPrimitives(
+ const SwRect& rRect )
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSeq( 4 );
+
+ basegfx::BColor aLineColor = SwViewOption::GetDocBoundariesColor().getBColor();
+ double nLineLength = 100.0; // in Twips
+
+ Point aPoints[] = { rRect.TopLeft(), rRect.TopRight(), rRect.BottomRight(), rRect.BottomLeft() };
+ double const aXOffDirs[] = { 1.0, -1.0, -1.0, 1.0 };
+ double const aYOffDirs[] = { 1.0, 1.0, -1.0, -1.0 };
+
+ // Actually loop over the corners to create the two lines
+ for ( int i = 0; i < 4; i++ )
+ {
+ basegfx::B2DVector aHorizVector( aXOffDirs[i], 0.0 );
+ basegfx::B2DVector aVertVector( 0.0, aYOffDirs[i] );
+
+ basegfx::B2DPoint aBPoint( aPoints[i].getX(), aPoints[i].getY() );
+
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append( aBPoint + aHorizVector * nLineLength );
+ aPolygon.append( aBPoint );
+ aPolygon.append( aBPoint + aVertVector * nLineLength );
+
+ aSeq[i] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aPolygon, aLineColor );
+ }
+
+ return aSeq;
+}
+
+void SwPageFrame::PaintSubsidiaryLines( const SwPageFrame *,
+ const SwRect & ) const
+{
+ if ( gProp.pSGlobalShell->IsHeaderFooterEdit() )
+ return;
+
+ const SwFrame* pLay = Lower();
+ const SwFrame* pFootnoteCont = nullptr;
+ const SwFrame* pPageBody = nullptr;
+ while ( pLay && !( pFootnoteCont && pPageBody ) )
+ {
+ if ( pLay->IsFootnoteContFrame( ) )
+ pFootnoteCont = pLay;
+ if ( pLay->IsBodyFrame() )
+ pPageBody = pLay;
+ pLay = pLay->GetNext();
+ }
+
+ SwRect aArea( pPageBody->getFrameArea() );
+ if ( pFootnoteCont )
+ aArea.AddBottom( pFootnoteCont->getFrameArea().Bottom() - aArea.Bottom() );
+
+ if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
+ ProcessPrimitives( lcl_CreatePageAreaDelimiterPrimitives( aArea ) );
+ else
+ ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
+}
+
+void SwColumnFrame::PaintSubsidiaryLines( const SwPageFrame *,
+ const SwRect & ) const
+{
+ const SwFrame* pLay = Lower();
+ const SwFrame* pFootnoteCont = nullptr;
+ const SwFrame* pColBody = nullptr;
+ while ( pLay && !( pFootnoteCont && pColBody ) )
+ {
+ if ( pLay->IsFootnoteContFrame( ) )
+ pFootnoteCont = pLay;
+ if ( pLay->IsBodyFrame() )
+ pColBody = pLay;
+ pLay = pLay->GetNext();
+ }
+
+ SwRect aArea( pColBody->getFrameArea() );
+
+ // #i3662# - enlarge top of column body frame's printing area
+ // in sections to top of section frame.
+ const bool bColInSection = GetUpper()->IsSctFrame();
+ if ( bColInSection )
+ {
+ if ( IsVertical() )
+ aArea.Right( GetUpper()->getFrameArea().Right() );
+ else
+ aArea.Top( GetUpper()->getFrameArea().Top() );
+ }
+
+ if ( pFootnoteCont )
+ aArea.AddBottom( pFootnoteCont->getFrameArea().Bottom() - aArea.Bottom() );
+
+ ::SwAlignRect( aArea, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+
+ if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
+ ProcessPrimitives( lcl_CreateColumnAreaDelimiterPrimitives( aArea ) );
+ else
+ ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
+}
+
+void SwSectionFrame::PaintSubsidiaryLines( const SwPageFrame * pPage,
+ const SwRect & rRect ) const
+{
+ const bool bNoLowerColumn = !Lower() || !Lower()->IsColumnFrame();
+ if ( bNoLowerColumn )
+ {
+ SwLayoutFrame::PaintSubsidiaryLines( pPage, rRect );
+ }
+}
+
+/**
+ * The SwBodyFrame doesn't print any subsidiary line: it's bounds are painted
+ * either by the parent page or the parent column frame.
+ */
+void SwBodyFrame::PaintSubsidiaryLines( const SwPageFrame *,
+ const SwRect & ) const
+{
+}
+
+void SwHeadFootFrame::PaintSubsidiaryLines( const SwPageFrame *, const SwRect & ) const
+{
+ if ( gProp.pSGlobalShell->IsHeaderFooterEdit() )
+ {
+ SwRect aArea( getFramePrintArea() );
+ aArea.Pos() += getFrameArea().Pos();
+ if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
+ ProcessPrimitives( lcl_CreatePageAreaDelimiterPrimitives( aArea ) );
+ else
+ ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
+ }
+}
+
+/**
+ * This method is overridden in order to have no subsidiary lines
+ * around the footnotes.
+ */
+void SwFootnoteFrame::PaintSubsidiaryLines( const SwPageFrame *,
+ const SwRect & ) const
+{
+}
+
+/**
+ * This method is overridden in order to have no subsidiary lines
+ * around the footnotes containers.
+ */
+void SwFootnoteContFrame::PaintSubsidiaryLines( const SwPageFrame *,
+ const SwRect & ) const
+{
+}
+
+void SwLayoutFrame::PaintSubsidiaryLines( const SwPageFrame *pPage,
+ const SwRect &rRect ) const
+{
+ bool bNewTableModel = false;
+
+ // #i29550#
+ if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
+ {
+ const SwTabFrame* pTabFrame = FindTabFrame();
+ if ( pTabFrame->IsCollapsingBorders() )
+ return;
+
+ bNewTableModel = pTabFrame->GetTable()->IsNewModel();
+ // in the new table model, we have an early return for all cell-related
+ // frames, except from non-covered table cells
+ if ( bNewTableModel )
+ if ( IsTabFrame() ||
+ IsRowFrame() ||
+ ( IsCellFrame() && IsCoveredCell() ) )
+ return;
+ }
+
+ const bool bFlys = pPage->GetSortedObjs() != nullptr;
+
+ const bool bCell = IsCellFrame();
+ // #i3662# - use frame area for cells for section use also frame area
+ const bool bUseFrameArea = bCell || IsSctFrame();
+ SwRect aOriginal( bUseFrameArea ? getFrameArea() : getFramePrintArea() );
+ if ( !bUseFrameArea )
+ aOriginal.Pos() += getFrameArea().Pos();
+
+ ::SwAlignRect( aOriginal, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+
+ if ( !aOriginal.Overlaps( rRect ) )
+ return;
+
+ SwRect aOut( aOriginal );
+ aOut.Intersection_( rRect );
+
+ const SwTwips nRight = aOut.Right();
+ const SwTwips nBottom= aOut.Bottom();
+
+ const Point aRT( nRight, aOut.Top() );
+ const Point aRB( nRight, nBottom );
+ const Point aLB( aOut.Left(), nBottom );
+
+ SubColFlags nSubColor = ( bCell || IsRowFrame() )
+ ? SubColFlags::Tab
+ : ( IsInSct()
+ ? SubColFlags::Sect
+ : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
+
+ // collect body, header, footer, footnote and section
+ // sub-lines in <pSpecSubsLine> array.
+ const bool bSpecialSublines = IsBodyFrame() || IsHeaderFrame() || IsFooterFrame() ||
+ IsFootnoteFrame() || IsSctFrame();
+ SwLineRects *const pUsedSubsLines = bSpecialSublines
+ ? gProp.pSSpecSubsLines.get() : gProp.pSSubsLines.get();
+
+ // NOTE: for cell frames only left and right (horizontal layout) respectively
+ // top and bottom (vertical layout) lines painted.
+ // NOTE2: this does not hold for the new table model!!! We paint the top border
+ // of each non-covered table cell.
+ const bool bVert = IsVertical();
+ if ( bFlys )
+ {
+ // add control for drawing left and right lines
+ if ( !bCell || bNewTableModel || !bVert )
+ {
+ if ( aOriginal.Left() == aOut.Left() )
+ ::lcl_RefreshLine( this, pPage, aOut.Pos(), aLB, nSubColor, pUsedSubsLines );
+ // in vertical layout set page/column break at right
+ if ( aOriginal.Right() == nRight )
+ ::lcl_RefreshLine( this, pPage, aRT, aRB, nSubColor, pUsedSubsLines );
+ }
+ // adjust control for drawing top and bottom lines
+ if ( !bCell || bNewTableModel || bVert )
+ {
+ if ( aOriginal.Top() == aOut.Top() )
+ // in horizontal layout set page/column break at top
+ ::lcl_RefreshLine( this, pPage, aOut.Pos(), aRT, nSubColor, pUsedSubsLines );
+ if ( aOriginal.Bottom() == nBottom )
+ ::lcl_RefreshLine( this, pPage, aLB, aRB, nSubColor,
+ pUsedSubsLines );
+ }
+ }
+ else
+ {
+ // add control for drawing left and right lines
+ if ( !bCell || bNewTableModel || !bVert )
+ {
+ if ( aOriginal.Left() == aOut.Left() )
+ {
+ const SwRect aRect( aOut.Pos(), aLB );
+ pUsedSubsLines->AddLineRect( aRect, nullptr,
+ SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
+ }
+ // in vertical layout set page/column break at right
+ if ( aOriginal.Right() == nRight )
+ {
+ const SwRect aRect( aRT, aRB );
+ pUsedSubsLines->AddLineRect( aRect, nullptr,
+ SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
+ }
+ }
+ // adjust control for drawing top and bottom lines
+ if ( !bCell || bNewTableModel || bVert )
+ {
+ if ( aOriginal.Top() == aOut.Top() )
+ {
+ // in horizontal layout set page/column break at top
+ const SwRect aRect( aOut.Pos(), aRT );
+ pUsedSubsLines->AddLineRect( aRect, nullptr,
+ SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
+ }
+ if ( aOriginal.Bottom() == nBottom )
+ {
+ const SwRect aRect( aLB, aRB );
+ pUsedSubsLines->AddLineRect( aRect, nullptr,
+ SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
+ }
+ }
+ }
+}
+
+/**
+ * Refreshes all extra data (line breaks a.s.o) of the page. Basically only those objects
+ * are considered which horizontally overlap the Rect.
+ */
+void SwPageFrame::RefreshExtraData( const SwRect &rRect ) const
+{
+ const SwLineNumberInfo &rInfo = GetFormat()->GetDoc()->GetLineNumberInfo();
+ bool bLineInFly = (rInfo.IsPaintLineNumbers() && rInfo.IsCountInFlys())
+ || static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos()) != text::HoriOrientation::NONE;
+
+ SwRect aRect( rRect );
+ ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+ if ( !aRect.HasArea() )
+ return;
+
+ SwLayoutFrame::RefreshExtraData( aRect );
+
+ if ( bLineInFly && GetSortedObjs() )
+ for (SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->getFrameArea().Top() <= aRect.Bottom() &&
+ pFly->getFrameArea().Bottom() >= aRect.Top() )
+ pFly->RefreshExtraData( aRect );
+ }
+ }
+}
+
+void SwLayoutFrame::RefreshExtraData( const SwRect &rRect ) const
+{
+
+ const SwLineNumberInfo &rInfo = GetFormat()->GetDoc()->GetLineNumberInfo();
+ bool bLineInBody = rInfo.IsPaintLineNumbers(),
+ bLineInFly = bLineInBody && rInfo.IsCountInFlys(),
+ bRedLine = static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos())!=text::HoriOrientation::NONE;
+
+ const SwContentFrame *pCnt = ContainsContent();
+ while ( pCnt && IsAnLower( pCnt ) )
+ {
+ if ( pCnt->IsTextFrame() && ( bRedLine ||
+ ( !pCnt->IsInTab() &&
+ ((bLineInBody && pCnt->IsInDocBody()) ||
+ (bLineInFly && pCnt->IsInFly())) ) ) &&
+ pCnt->getFrameArea().Top() <= rRect.Bottom() &&
+ pCnt->getFrameArea().Bottom() >= rRect.Top() )
+ {
+ static_cast<const SwTextFrame*>(pCnt)->PaintExtraData( rRect );
+ }
+ if ( bLineInFly && pCnt->GetDrawObjs() )
+ for (SwAnchoredObject* pAnchoredObj : *pCnt->GetDrawObjs())
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() &&
+ pFly->getFrameArea().Top() <= rRect.Bottom() &&
+ pFly->getFrameArea().Bottom() >= rRect.Top() )
+ pFly->RefreshExtraData( rRect );
+ }
+ }
+ pCnt = pCnt->GetNextContentFrame();
+ }
+}
+
+/**
+ * For #102450#
+ * Determine the color, that is respectively will be drawn as background
+ * for the page frame.
+ * Using existing method SwFrame::GetBackgroundBrush to determine the color
+ * that is set at the page frame respectively is parent. If none is found
+ * return the global retouche color
+ *
+ * @return Color
+ */
+Color SwPageFrame::GetDrawBackgroundColor() const
+{
+ const SvxBrushItem* pBrushItem;
+ std::optional<Color> xDummyColor;
+ SwRect aDummyRect;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ if ( GetBackgroundBrush( aFillAttributes, pBrushItem, xDummyColor, aDummyRect, true, /*bConsiderTextBox=*/false) )
+ {
+ if(aFillAttributes && aFillAttributes->isUsed())
+ {
+ // let SdrAllFillAttributesHelper do the average color calculation
+ return Color(aFillAttributes->getAverageColor(aGlobalRetoucheColor.getBColor()));
+ }
+ else if(pBrushItem)
+ {
+ OUString referer;
+ SwViewShell * sh1 = getRootFrame()->GetCurrShell();
+ if (sh1 != nullptr) {
+ SfxObjectShell * sh2 = sh1->GetDoc()->GetPersist();
+ if (sh2 != nullptr && sh2->HasName()) {
+ referer = sh2->GetMedium()->GetName();
+ }
+ }
+ const Graphic* pGraphic = pBrushItem->GetGraphic(referer);
+
+ if(pGraphic)
+ {
+ // #29105# when a graphic is set, it may be possible to calculate a single
+ // color which looks good in all places of the graphic. Since it is
+ // planned to have text edit on the overlay one day and the fallback
+ // to aGlobalRetoucheColor returns something useful, just use that
+ // for now.
+ }
+ else
+ {
+ // not a graphic, use (hopefully) initialized color
+ return pBrushItem->GetColor();
+ }
+ }
+ }
+
+ return aGlobalRetoucheColor;
+}
+
+/// create/return font used to paint the "empty page" string
+const vcl::Font& SwPageFrame::GetEmptyPageFont()
+{
+ static vcl::Font aEmptyPgFont = []()
+ {
+ vcl::Font tmp;
+ tmp.SetFontSize( Size( 0, 80 * 20 )); // == 80 pt
+ tmp.SetWeight( WEIGHT_BOLD );
+ tmp.SetStyleName(OUString());
+ tmp.SetFamilyName("Helvetica");
+ tmp.SetFamily( FAMILY_SWISS );
+ tmp.SetTransparent( true );
+ tmp.SetColor( COL_GRAY );
+ return tmp;
+ }();
+
+ return aEmptyPgFont;
+}
+
+/**
+ * Retouch for a section
+ *
+ * Retouch will only be done, if the Frame is the last one in his chain.
+ * The whole area of the upper which is located below the Frame will be
+ * cleared using PaintSwFrameBackground.
+ */
+void SwFrame::Retouch( const SwPageFrame * pPage, const SwRect &rRect ) const
+{
+ if ( gProp.bSFlyMetafile )
+ return;
+
+ OSL_ENSURE( GetUpper(), "Retouche try without Upper." );
+ OSL_ENSURE( getRootFrame()->GetCurrShell() && gProp.pSGlobalShell->GetWin(), "Retouche on a printer?" );
+
+ SwRect aRetouche( GetUpper()->GetPaintArea() );
+ aRetouche.Top( getFrameArea().Top() + getFrameArea().Height() );
+ aRetouche.Intersection( gProp.pSGlobalShell->VisArea() );
+
+ if ( aRetouche.HasArea() )
+ {
+ //Omit the passed Rect. To do this, we unfortunately need a region to
+ //cut out.
+ SwRegionRects aRegion( aRetouche );
+ aRegion -= rRect;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );
+
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ {
+ const SwRect &rRetouche = aRegion[i];
+
+ GetUpper()->PaintBaBo( rRetouche, pPage );
+
+ //Hell and Heaven need to be refreshed too.
+ //To avoid recursion my retouch flag needs to be reset first!
+ ResetRetouche();
+ if ( rRetouche.HasArea() )
+ {
+ const Color aPageBackgrdColor(pPage->GetDrawBackgroundColor());
+ const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
+ // --> OD #i76669#
+ SwViewObjectContactRedirector aSwRedirector( *pSh );
+ // <--
+
+ pSh->Imp()->PaintLayer( rIDDMA.GetHellId(), nullptr,
+ *pPage, rRetouche, &aPageBackgrdColor,
+ pPage->IsRightToLeft(),
+ &aSwRedirector );
+ pSh->Imp()->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
+ *pPage, rRetouche, &aPageBackgrdColor,
+ pPage->IsRightToLeft(),
+ &aSwRedirector );
+ }
+
+ SetRetouche();
+
+ //Because we leave all paint areas, we need to refresh the
+ //subsidiary lines.
+ pPage->RefreshSubsidiary( rRetouche );
+ }
+ }
+ if ( SwViewShell::IsLstEndAction() )
+ ResetRetouche();
+}
+
+/**
+ * Determine the background brush for the frame:
+ * the background brush is taken from it-self or from its parent (anchor/upper).
+ * Normally, the background brush is taken, which has no transparent color or
+ * which has a background graphic. But there are some special cases:
+ * (1) No background brush is taken from a page frame, if view option "IsPageBack"
+ * isn't set.
+ * (2) Background brush from an index section is taken under special conditions.
+ * In this case parameter <rpCol> is set to the index shading color.
+ * (3) New (OD 20.08.2002) - Background brush is taken, if on background drawing
+ * of the frame transparency is considered and its color is not "no fill"/"auto fill"
+ *
+ * Old description in German:
+ * Returns the Backgroundbrush for the area of the Frame.
+ * The Brush is defined by the Frame or by an upper, the first Brush is
+ * used. If no Brush is defined for a Frame, false is returned.
+ *
+ * @param rpBrush
+ * output parameter - constant reference pointer the found background brush
+ *
+ * @param rpFillStyle
+ * output parameter - constant reference pointer the found background fill style
+ *
+ * @param rpFillGradient
+ * output parameter - constant reference pointer the found background fill gradient
+ *
+ * @param rpCol
+ * output parameter - constant reference pointer to the color of the index shading
+ * set under special conditions, if background brush is taken from an index section.
+ *
+ * @param rOrigRect
+ * in-/output parameter - reference to the rectangle the background brush is
+ * considered for - adjusted to the frame, from which the background brush is
+ * taken.
+ *
+ * @parem bLowerMode
+ * input parameter - boolean indicating, if background brush should *not* be
+ * taken from parent.
+ *
+ * @param bConsiderTextBox
+ * consider the TextBox of this fly frame (if there is any) when determining
+ * the background color, useful for automatic font color.
+ *
+ * @return true, if a background brush for the frame is found
+ */
+bool SwFrame::GetBackgroundBrush(
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
+ const SvxBrushItem* & rpBrush,
+ std::optional<Color>& rxCol,
+ SwRect &rOrigRect,
+ bool bLowerMode,
+ bool bConsiderTextBox ) const
+{
+ const SwFrame *pFrame = this;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const SwViewOption *pOpt = pSh->GetViewOptions();
+ rpBrush = nullptr;
+ rxCol.reset();
+ do
+ {
+ if ( pFrame->IsPageFrame() && !pOpt->IsPageBack() )
+ return false;
+
+ if (pFrame->supportsFullDrawingLayerFillAttributeSet())
+ {
+ bool bHandledTextBox = false;
+ if (pFrame->IsFlyFrame() && bConsiderTextBox)
+ {
+ const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(pFrame);
+ SwFrameFormat* pShape
+ = SwTextBoxHelper::getOtherTextBoxFormat(pFlyFrame->GetFormat(), RES_FLYFRMFMT);
+ if (pShape)
+ {
+ SdrObject* pObject = pShape->FindRealSdrObject();
+ if (pObject)
+ {
+ // Work with the fill attributes of the shape of the fly frame.
+ rFillAttributes =
+ std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(
+ pObject->GetMergedItemSet());
+ bHandledTextBox = true;
+ }
+ }
+ }
+
+ if (!bHandledTextBox)
+ rFillAttributes = pFrame->getSdrAllFillAttributesHelper();
+ }
+ const SvxBrushItem &rBack = pFrame->GetAttrSet()->GetBackground();
+
+ if( pFrame->IsSctFrame() )
+ {
+ const SwSection* pSection = static_cast<const SwSectionFrame*>(pFrame)->GetSection();
+ // Note: If frame <pFrame> is a section of the index and
+ // it its background color is "no fill"/"auto fill" and
+ // it has no background graphic and
+ // we are not in the page preview and
+ // we are not in read-only mode and
+ // option "index shadings" is set and
+ // the output is not the printer
+ // then set <rpCol> to the color of the index shading
+ if( pSection && ( SectionType::ToxHeader == pSection->GetType() ||
+ SectionType::ToxContent == pSection->GetType() ) &&
+ (rBack.GetColor() == COL_TRANSPARENT) &&
+ rBack.GetGraphicPos() == GPOS_NONE &&
+ !pOpt->IsPagePreview() &&
+ !pOpt->IsReadonly() &&
+ // #114856# Form view
+ !pOpt->IsFormView() &&
+ SwViewOption::IsIndexShadings() &&
+ !pOpt->IsPDFExport() &&
+ pSh->GetOut()->GetOutDevType() != OUTDEV_PRINTER )
+ {
+ rxCol = SwViewOption::GetIndexShadingsColor();
+ }
+ }
+
+ // determine, if background draw of frame <pFrame> considers transparency
+ // Status Quo: background transparency have to be
+ // considered for fly frames
+ const bool bConsiderBackgroundTransparency = pFrame->IsFlyFrame();
+
+ // #i125189# Do not base the decision for using the parent's fill style for this
+ // frame when the new DrawingLayer FillAttributes are used on the SdrAllFillAttributesHelper
+ // information. There the data is already optimized to no fill in the case that the
+ // transparence is at 100% while no fill is the criteria for derivation
+ bool bNewDrawingLayerFillStyleIsUsedAndNotNoFill(false);
+
+ if(rFillAttributes)
+ {
+ // the new DrawingLayer FillStyle is used
+ if(rFillAttributes->isUsed())
+ {
+ // it's not drawing::FillStyle_NONE
+ bNewDrawingLayerFillStyleIsUsedAndNotNoFill = true;
+ }
+ else
+ {
+ // maybe optimized already when 100% transparency is used somewhere, need to test
+ // XFillStyleItem directly from the model data
+ const drawing::FillStyle eFillStyle(pFrame->GetAttrSet()->Get(XATTR_FILLSTYLE).GetValue());
+
+ if(drawing::FillStyle_NONE != eFillStyle)
+ {
+ bNewDrawingLayerFillStyleIsUsedAndNotNoFill = true;
+ }
+ }
+ }
+
+ // add condition:
+ // If <bConsiderBackgroundTransparency> is set - see above -,
+ // return brush of frame <pFrame>, if its color is *not* "no fill"/"auto fill"
+ if (
+ // #i125189# Done when the new DrawingLayer FillAttributes are used and
+ // not drawing::FillStyle_NONE (see above)
+ bNewDrawingLayerFillStyleIsUsedAndNotNoFill ||
+
+ // done when SvxBrushItem is used
+ rBack.GetColor().GetAlpha() == 255 || rBack.GetGraphicPos() != GPOS_NONE ||
+
+ // done when direct color is forced
+ rxCol ||
+
+ // done when consider BG transparency and color is not completely transparent
+ (bConsiderBackgroundTransparency && (rBack.GetColor() != COL_TRANSPARENT))
+ )
+ {
+ rpBrush = &rBack;
+ if ( pFrame->IsPageFrame() && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ rOrigRect = pFrame->getFrameArea();
+ ::SwAlignRect(rOrigRect, pSh, pSh->GetOut());
+ }
+ else
+ {
+ if (pFrame->IsPageFrame()
+ && pFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue())
+ {
+ rOrigRect = pFrame->getFrameArea();
+ }
+ else if (pFrame->getFrameArea().SSize() != pFrame->getFramePrintArea().SSize())
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ ::lcl_CalcBorderRect( rOrigRect, pFrame, rAttrs, false, gProp );
+ }
+ else
+ {
+ rOrigRect = pFrame->getFramePrintArea();
+ rOrigRect += pFrame->getFrameArea().Pos();
+ }
+ }
+
+ return true;
+ }
+
+ if ( bLowerMode )
+ {
+ // Do not try to get background brush from parent (anchor/upper)
+ return false;
+ }
+
+ // get parent frame - anchor or upper - for next loop
+ if ( pFrame->IsFlyFrame() )
+ {
+ pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
+ }
+ else
+ {
+ pFrame = pFrame->GetUpper();
+ }
+ } while ( pFrame );
+
+ return false;
+}
+
+void SetOutDevAndWin( SwViewShell *pSh, OutputDevice *pO,
+ vcl::Window *pW, sal_uInt16 nZoom )
+{
+ pSh->mpOut = pO;
+ pSh->mpWin = pW;
+ pSh->mpOpt->SetZoom( nZoom );
+}
+
+Graphic SwFrameFormat::MakeGraphic( ImageMap*, const sal_uInt32 /*nMaximumQuadraticPixels*/, const std::optional<Size>& /*rTargetDPI*/ )
+{
+ return Graphic();
+}
+
+Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap, const sal_uInt32 /*nMaximumQuadraticPixels*/, const std::optional<Size>& /*rTargetDPI*/ )
+{
+ Graphic aRet;
+ //search any Fly!
+ SwIterator<SwFrame,SwFormat> aIter( *this );
+ SwFrame *pFirst = aIter.First();
+ SwViewShell *const pSh =
+ pFirst ? pFirst->getRootFrame()->GetCurrShell() : nullptr;
+ if (nullptr != pSh)
+ {
+ SwViewShell *pOldGlobal = gProp.pSGlobalShell;
+ gProp.pSGlobalShell = pSh;
+
+ bool bNoteURL = pMap &&
+ SfxItemState::SET != GetAttrSet().GetItemState( RES_URL );
+ if( bNoteURL )
+ {
+ OSL_ENSURE( !pNoteURL, "MakeGraphic: pNoteURL already used? " );
+ pNoteURL = new SwNoteURL;
+ }
+ SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pFirst);
+
+ OutputDevice *pOld = pSh->GetOut();
+ ScopedVclPtrInstance< VirtualDevice > pDev( *pOld );
+ pDev->EnableOutput( false );
+
+ GDIMetaFile aMet;
+ MapMode aMap( pOld->GetMapMode().GetMapUnit() );
+ pDev->SetMapMode( aMap );
+ aMet.SetPrefMapMode( aMap );
+
+ ::SwCalcPixStatics( pSh->GetOut() );
+ aMet.SetPrefSize( pFly->getFrameArea().SSize() );
+
+ aMet.Record( pDev.get() );
+ pDev->SetLineColor();
+ pDev->SetFillColor();
+ pDev->SetFont( pOld->GetFont() );
+
+ //Enlarge the rectangle if needed, so the border is painted too.
+ SwRect aOut( pFly->getFrameArea() );
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFly );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ if ( rAttrs.CalcRightLine() )
+ aOut.AddWidth(2*gProp.nSPixelSzW );
+ if ( rAttrs.CalcBottomLine() )
+ aOut.AddHeight(2*gProp.nSPixelSzH );
+
+ // #i92711# start Pre/PostPaint encapsulation before pOut is changed to the buffering VDev
+ const vcl::Region aRepaintRegion(aOut.SVRect());
+ pSh->DLPrePaint2(aRepaintRegion);
+
+ vcl::Window *pWin = pSh->GetWin();
+ sal_uInt16 nZoom = pSh->GetViewOptions()->GetZoom();
+ ::SetOutDevAndWin( pSh, pDev, nullptr, 100 );
+ gProp.bSFlyMetafile = true;
+ gProp.pSFlyMetafileOut = pWin->GetOutDev();
+
+ SwViewShellImp *pImp = pSh->Imp();
+ gProp.pSFlyOnlyDraw = pFly;
+ gProp.pSLines.reset(new SwLineRects);
+
+ // determine page, fly frame is on
+ const SwPageFrame* pFlyPage = pFly->FindPageFrame();
+ const Color aPageBackgrdColor(pFlyPage->GetDrawBackgroundColor());
+ const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
+ // --> OD #i76669#
+ SwViewObjectContactRedirector aSwRedirector( *pSh );
+ // <--
+ pImp->PaintLayer( rIDDMA.GetHellId(), nullptr,
+ *pFlyPage, aOut, &aPageBackgrdColor,
+ pFlyPage->IsRightToLeft(),
+ &aSwRedirector );
+ gProp.pSLines->PaintLines( pDev, gProp );
+ if ( pFly->IsFlyInContentFrame() )
+ pFly->PaintSwFrame( *pDev, aOut );
+ gProp.pSLines->PaintLines( pDev, gProp );
+ pImp->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
+ *pFlyPage, aOut, &aPageBackgrdColor,
+ pFlyPage->IsRightToLeft(),
+ &aSwRedirector );
+ gProp.pSLines->PaintLines( pDev, gProp );
+ gProp.pSLines.reset();
+ gProp.pSFlyOnlyDraw = nullptr;
+
+ gProp.pSFlyMetafileOut = nullptr;
+ gProp.bSFlyMetafile = false;
+ ::SetOutDevAndWin( pSh, pOld, pWin, nZoom );
+
+ // #i92711# end Pre/PostPaint encapsulation when pOut is back and content is painted
+ pSh->DLPostPaint2(true);
+
+ aMet.Stop();
+ aMet.Move( -pFly->getFrameArea().Left(), -pFly->getFrameArea().Top() );
+ aRet = Graphic( aMet );
+
+ if( bNoteURL )
+ {
+ OSL_ENSURE( pNoteURL, "MakeGraphic: Good Bye, NoteURL." );
+ delete pNoteURL;
+ pNoteURL = nullptr;
+ }
+ gProp.pSGlobalShell = pOldGlobal;
+ }
+ return aRet;
+}
+
+Graphic SwDrawFrameFormat::MakeGraphic( ImageMap*, const sal_uInt32 nMaximumQuadraticPixels, const std::optional<Size>& rTargetDPI )
+{
+ Graphic aRet;
+ SwDrawModel* pMod = getIDocumentDrawModelAccess().GetDrawModel();
+ if ( pMod )
+ {
+ SdrObject *pObj = FindSdrObject();
+ SdrView aView( *pMod );
+ SdrPageView *pPgView = aView.ShowSdrPage(aView.GetModel()->GetPage(0));
+ aView.MarkObj( pObj, pPgView );
+ aRet = aView.GetMarkedObjBitmapEx(/*bNoVDevIfOneBmpMarked=*/false, nMaximumQuadraticPixels, rTargetDPI);
+ aView.HideSdrPage();
+ }
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/sectfrm.cxx b/sw/source/core/layout/sectfrm.cxx
new file mode 100644
index 000000000..eb667dd51
--- /dev/null
+++ b/sw/source/core/layout/sectfrm.cxx
@@ -0,0 +1,2941 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <svl/itemiter.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <fmtclbl.hxx>
+#include <sectfrm.hxx>
+#include <cellfrm.hxx>
+#include <section.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <txtfrm.hxx>
+#include <fmtclds.hxx>
+#include <colfrm.hxx>
+#include <tabfrm.hxx>
+#include <ftnfrm.hxx>
+#include <layouter.hxx>
+#include <dbg_lay.hxx>
+#include <viewopt.hxx>
+#include <viewimp.hxx>
+#include <editeng/brushitem.hxx>
+#include <fmtftntx.hxx>
+#include <flyfrm.hxx>
+#include <sortedobjs.hxx>
+#include <hints.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+
+namespace
+{
+/**
+ * Performs the correct type of position invalidation depending on if we're in
+ * CalcContent().
+ */
+void InvalidateFramePos(SwFrame* pFrame, bool bInCalcContent)
+{
+ if (bInCalcContent)
+ pFrame->InvalidatePos_();
+ else
+ pFrame->InvalidatePos();
+}
+}
+
+SwSectionFrame::SwSectionFrame( SwSection &rSect, SwFrame* pSib )
+ : SwLayoutFrame( rSect.GetFormat(), pSib )
+ , SwFlowFrame( static_cast<SwFrame&>(*this) )
+ , m_pSection( &rSect )
+ , m_bFootnoteAtEnd(false)
+ , m_bEndnAtEnd(false)
+ , m_bContentLock(false)
+ , m_bOwnFootnoteNum(false)
+ , m_bFootnoteLock(false)
+{
+ StartListening(rSect.GetFormat()->GetNotifier());
+
+ mnFrameType = SwFrameType::Section;
+
+ CalcFootnoteAtEndFlag();
+ CalcEndAtEndFlag();
+}
+
+SwSectionFrame::SwSectionFrame( SwSectionFrame &rSect, bool bMaster ) :
+ SwLayoutFrame( rSect.GetFormat(), rSect.getRootFrame() ),
+ SwFlowFrame( static_cast<SwFrame&>(*this) ),
+ m_pSection( rSect.GetSection() ),
+ m_bFootnoteAtEnd( rSect.IsFootnoteAtEnd() ),
+ m_bEndnAtEnd( rSect.IsEndnAtEnd() ),
+ m_bContentLock( false ),
+ m_bOwnFootnoteNum( false ),
+ m_bFootnoteLock( false )
+{
+ StartListening(rSect.GetFormat()->GetNotifier());
+
+ mnFrameType = SwFrameType::Section;
+
+ PROTOCOL( this, PROT::Section, bMaster ? DbgAction::CreateMaster : DbgAction::CreateFollow, &rSect )
+
+ if( bMaster )
+ {
+ SwSectionFrame* pMaster = rSect.IsFollow() ? rSect.FindMaster() : nullptr;
+ if (pMaster)
+ pMaster->SetFollow( this );
+ SetFollow( &rSect );
+ }
+ else
+ {
+ SetFollow( rSect.GetFollow() );
+ rSect.SetFollow( this );
+ if( !GetFollow() )
+ rSect.SimpleFormat();
+ if( !rSect.IsColLocked() )
+ rSect.InvalidateSize();
+ }
+}
+
+// NOTE: call <SwSectionFrame::Init()> directly after creation of a new section
+// frame and its insert in the layout.
+void SwSectionFrame::Init()
+{
+ assert(GetUpper() && "SwSectionFrame::Init before insertion?!");
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetWidth( aFrm, nWidth );
+ aRectFnSet.SetHeight( aFrm, 0 );
+ }
+
+ // #109700# LRSpace for sections
+ const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetLeft( aPrt, rLRSpace.GetLeft() );
+ aRectFnSet.SetWidth( aPrt, nWidth - rLRSpace.GetLeft() - rLRSpace.GetRight() );
+ aRectFnSet.SetHeight( aPrt, 0 );
+ }
+
+ const SwFormatCol &rCol = GetFormat()->GetCol();
+ if( ( rCol.GetNumCols() > 1 || IsAnyNoteAtEnd() ) && !IsInFootnote() )
+ {
+ const SwFormatCol *pOld = Lower() ? &rCol : new SwFormatCol;
+ ChgColumns( *pOld, rCol, IsAnyNoteAtEnd() );
+ if( pOld != &rCol )
+ delete pOld;
+ }
+}
+
+void SwSectionFrame::DestroyImpl()
+{
+ if( GetFormat() && !GetFormat()->GetDoc()->IsInDtor() )
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame )
+ pRootFrame->RemoveFromList( this );
+ if( IsFollow() )
+ {
+ SwSectionFrame *pMaster = FindMaster();
+ if( pMaster )
+ {
+ PROTOCOL( this, PROT::Section, DbgAction::DelFollow, pMaster )
+ pMaster->SetFollow( GetFollow() );
+ // A Master always grabs the space until the lower edge of his
+ // Upper. If he doesn't have a Follow anymore, he can
+ // release it, which is why the Size of the Master is
+ // invalidated.
+ if( !GetFollow() )
+ pMaster->InvalidateSize();
+ }
+ }
+#if defined DBG_UTIL
+ else if( HasFollow() )
+ {
+ PROTOCOL( this, PROT::Section, DbgAction::DelMaster, GetFollow() )
+ }
+#endif
+ }
+
+ SwLayoutFrame::DestroyImpl();
+}
+
+SwSectionFrame::~SwSectionFrame()
+{
+}
+
+void SwSectionFrame::DelEmpty( bool bRemove )
+{
+ if( IsColLocked() )
+ {
+ OSL_ENSURE( !bRemove, "Don't delete locked SectionFrames" );
+ return;
+ }
+ SwFrame* pUp = GetUpper();
+ if( pUp )
+ {
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for current next paragraph will change
+ // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = FindNextCnt( true );
+ auto pPrev = FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ Cut_( bRemove );
+ }
+ SwSectionFrame *pMaster = IsFollow() ? FindMaster() : nullptr;
+ if (pMaster)
+ {
+ pMaster->SetFollow( GetFollow() );
+ // A Master always grabs the space until the lower edge of his
+ // Upper. If he doesn't have a Follow anymore, he can
+ // release it, which is why the Size of the Master is
+ // invalidated.
+ if( !GetFollow() && !pMaster->IsColLocked() )
+ pMaster->InvalidateSize();
+ }
+ SetFollow(nullptr);
+ if( !pUp )
+ return;
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( 0 );
+ }
+
+ // If we are destroyed immediately anyway, we don't need
+ // to put us into the list
+ if( bRemove )
+ { // If we already were half dead before this DelEmpty,
+ // we are likely in the list and have to remove us from
+ // it
+ if( !m_pSection && getRootFrame() )
+ getRootFrame()->RemoveFromList( this );
+ }
+ else if( getRootFrame() )
+ {
+ getRootFrame()->InsertEmptySct( this );
+ }
+
+ m_pSection = nullptr; // like this a reanimation is virtually impossible though
+}
+
+void SwSectionFrame::Cut()
+{
+ Cut_( true );
+}
+
+void SwSectionFrame::Cut_( bool bRemove )
+{
+ OSL_ENSURE( GetUpper(), "Cut without Upper()." );
+
+ PROTOCOL( this, PROT::Cut, DbgAction::NONE, GetUpper() )
+
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+ SwFrame *pFrame = GetNext();
+ SwFrame* pPrepFrame = nullptr;
+ while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
+ pFrame = pFrame->GetNext();
+ if( pFrame )
+ { // The former successor might have calculated a gap to the predecessor
+ // which is now obsolete since he becomes the first
+ pFrame->InvalidatePrt_();
+ pFrame->InvalidatePos_();
+ if( pFrame->IsSctFrame() )
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if ( pFrame && pFrame->IsContentFrame() )
+ {
+ pFrame->InvalidatePage( pPage );
+ if( IsInFootnote() && !GetIndPrev() )
+ pPrepFrame = pFrame;
+ }
+ }
+ else
+ {
+ InvalidateNextPos();
+ // Someone has to take over the retouching: predecessor or Upper
+ pFrame = GetPrev();
+ if ( nullptr != pFrame )
+ {
+ pFrame->SetRetouche();
+ pFrame->Prepare( PrepareHint::WidowsOrphans );
+ if ( pFrame->IsContentFrame() )
+ pFrame->InvalidatePage( pPage );
+ }
+ // If I am (was) the only FlowFrame in my Upper, then he has to take over
+ // the retouching.
+ // Furthermore a blank page could have emerged
+ else
+ { SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
+ pRoot->SetSuperfluous();
+ GetUpper()->SetCompletePaint();
+ }
+ }
+ // First remove, then shrink Upper
+ SwLayoutFrame *pUp = GetUpper();
+ if( bRemove )
+ {
+ RemoveFromLayout();
+ if( pUp && !pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked() &&
+ pUp->GetUpper() )
+ {
+ pUp->Cut();
+ SwFrame::DestroyFrame(pUp);
+ pUp = nullptr;
+ }
+ }
+ if( pPrepFrame )
+ pPrepFrame->Prepare( PrepareHint::FootnoteInvalidation );
+ if ( !pUp )
+ return;
+
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nFrameHeight <= 0 )
+ return;
+
+ if( !bRemove )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, 0 );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, 0 );
+ }
+
+ pUp->Shrink( nFrameHeight );
+}
+
+void SwSectionFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
+{
+ OSL_ENSURE( pParent, "No parent for Paste()." );
+ OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
+ OSL_ENSURE( pParent != this, "I'm my own parent." );
+ OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
+ OSL_ENSURE( !GetPrev() && !GetUpper(),
+ "I am still registered somewhere." );
+
+ PROTOCOL( this, PROT::Paste, DbgAction::NONE, GetUpper() )
+
+ // Add to the tree
+ SwSectionFrame* pSect = pParent->FindSctFrame();
+ // Assure that parent is not inside a table frame, which is inside the found section frame.
+ if ( pSect )
+ {
+ SwTabFrame* pTableFrame = pParent->FindTabFrame();
+ if ( pTableFrame &&
+ pSect->IsAnLower( pTableFrame ) )
+ {
+ pSect = nullptr;
+ }
+ }
+
+ SwRectFnSet aRectFnSet(pParent);
+ if( pSect && HasToBreak( pSect ) )
+ {
+ if( pParent->IsColBodyFrame() ) // dealing with a single-column area
+ {
+ // If we are coincidentally at the end of a column, pSibling
+ // has to point to the first frame of the next column in order
+ // for the content of the next column to be moved correctly to the
+ // newly created pSect by the InsertGroup
+ SwColumnFrame *pCol = static_cast<SwColumnFrame*>(pParent->GetUpper());
+ while( !pSibling && nullptr != ( pCol = static_cast<SwColumnFrame*>(pCol->GetNext()) ) )
+ pSibling = static_cast<SwLayoutFrame*>(pCol->Lower())->Lower();
+ if( pSibling )
+ {
+ // Even worse: every following column content has to
+ // be attached to the pSibling-chain in order to be
+ // taken along
+ SwFrame *pTmp = pSibling;
+ while ( nullptr != ( pCol = static_cast<SwColumnFrame*>(pCol->GetNext()) ) )
+ {
+ while ( pTmp->GetNext() )
+ pTmp = pTmp->GetNext();
+ SwFrame* pSave = ::SaveContent( pCol );
+ if (pSave)
+ ::RestoreContent( pSave, pSibling->GetUpper(), pTmp );
+ }
+ }
+ }
+ pParent = pSect;
+ pSect = new SwSectionFrame( *static_cast<SwSectionFrame*>(pParent)->GetSection(), pParent );
+ // if pParent is decomposed into two parts, its Follow has to be attached
+ // to the new second part
+ pSect->SetFollow( static_cast<SwSectionFrame*>(pParent)->GetFollow() );
+ static_cast<SwSectionFrame*>(pParent)->SetFollow( nullptr );
+ if( pSect->GetFollow() )
+ pParent->InvalidateSize_();
+
+ const bool bInserted = InsertGroupBefore( pParent, pSibling, pSect );
+ if (bInserted)
+ {
+ pSect->Init();
+ aRectFnSet.MakePos( *pSect, pSect->GetUpper(), pSect->GetPrev(), true);
+ }
+ if( !static_cast<SwLayoutFrame*>(pParent)->Lower() )
+ {
+ SwSectionFrame::MoveContentAndDelete( static_cast<SwSectionFrame*>(pParent), false );
+ pParent = this;
+ }
+ }
+ else
+ InsertGroupBefore( pParent, pSibling, nullptr );
+
+ InvalidateAll_();
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+
+ if ( pSibling )
+ {
+ pSibling->InvalidatePos_();
+ pSibling->InvalidatePrt_();
+ if ( pSibling->IsContentFrame() )
+ pSibling->InvalidatePage( pPage );
+ }
+
+ SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nFrameHeight )
+ pParent->Grow( nFrameHeight );
+
+ if ( GetPrev() && !IsFollow() )
+ {
+ GetPrev()->InvalidateSize();
+ if ( GetPrev()->IsContentFrame() )
+ GetPrev()->InvalidatePage( pPage );
+ }
+}
+
+/**
+|* Here it's decided whether the this-SectionFrame should break up
+|* the passed (Section)frm (or not).
+|* Initially, all superior sections are broken up. Later on that could
+|* be made configurable.
+|*/
+bool SwSectionFrame::HasToBreak( const SwFrame* pFrame ) const
+{
+ if( !pFrame->IsSctFrame() )
+ return false;
+
+ const SwSectionFormat *pTmp = static_cast<const SwSectionFormat*>(GetFormat());
+
+ const SwFrameFormat *pOtherFormat = static_cast<const SwSectionFrame*>(pFrame)->GetFormat();
+ do
+ {
+ pTmp = pTmp->GetParent();
+ if( !pTmp )
+ return false;
+ if( pTmp == pOtherFormat )
+ return true;
+ } while( true ); // ( pTmp->GetSect().GetValue() );
+}
+
+/**
+|* Merges two SectionFrames, in case it's about the same section.
+|* This can be necessary when a (sub)section is deleted that had
+|* divided another part into two.
+|*/
+void SwSectionFrame::MergeNext( SwSectionFrame* pNxt )
+{
+ if (pNxt->IsDeleteForbidden())
+ return;
+
+ if (pNxt->IsJoinLocked() || GetSection() != pNxt->GetSection())
+ return;
+
+ PROTOCOL( this, PROT::Section, DbgAction::Merge, pNxt )
+
+ SwFrame* pTmp = ::SaveContent( pNxt );
+ if( pTmp )
+ {
+ SwFrame* pLast = Lower();
+ SwLayoutFrame* pLay = this;
+ if( pLast )
+ {
+ while( pLast->GetNext() )
+ pLast = pLast->GetNext();
+ if( pLast->IsColumnFrame() )
+ { // Columns now with BodyFrame
+ pLay = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLast)->Lower());
+ pLast = pLay->Lower();
+ if( pLast )
+ while( pLast->GetNext() )
+ pLast = pLast->GetNext();
+ }
+ }
+ ::RestoreContent( pTmp, pLay, pLast );
+ }
+ SetFollow( pNxt->GetFollow() );
+ pNxt->SetFollow( nullptr );
+ pNxt->Cut();
+ SwFrame::DestroyFrame(pNxt);
+ InvalidateSize();
+}
+
+/**
+|* Divides a SectionFrame into two parts. The second one starts with the
+|* passed frame.
+|* This is required when inserting an inner section, because the MoveFwd
+|* cannot have the desired effect within a frame or a table cell.
+|*/
+bool SwSectionFrame::SplitSect( SwFrame* pFrame, bool bApres )
+{
+ assert(pFrame && "SplitSect: Why?");
+ SwFrame* pOther = bApres ? pFrame->FindNext() : pFrame->FindPrev();
+ if( !pOther )
+ return false;
+ SwSectionFrame* pSect = pOther->FindSctFrame();
+ if( pSect != this )
+ return false;
+ // Put the content aside
+ SwFrame* pSav = ::SaveContent( this, bApres ? pOther : pFrame );
+ OSL_ENSURE( pSav, "SplitSect: What's on?" );
+ if( pSav ) // be robust
+ { // Create a new SctFrame, not as a Follower/master
+ SwSectionFrame* pNew = new SwSectionFrame( *pSect->GetSection(), pSect );
+ pNew->InsertBehind( pSect->GetUpper(), pSect );
+ pNew->Init();
+ SwRectFnSet aRectFnSet(this);
+ aRectFnSet.MakePos( *pNew, nullptr, pSect, true );
+ // OD 25.03.2003 #108339# - restore content:
+ // determine layout frame for restoring content after the initialization
+ // of the section frame. In the section initialization the columns are
+ // created.
+ {
+ SwLayoutFrame* pLay = pNew;
+ // Search for last layout frame, e.g. for columned sections.
+ while( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
+ pLay = static_cast<SwLayoutFrame*>(pLay->Lower());
+ ::RestoreContent( pSav, pLay, nullptr );
+ }
+ InvalidateSize_();
+ if( HasFollow() )
+ {
+ pNew->SetFollow( GetFollow() );
+ SetFollow( nullptr );
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+|* MoveContent is called for destroying a SectionFrames, due to
+|* the cancellation or hiding of a section, to handle the content.
+|* If the SectionFrame hasn't broken up another one, then the content
+|* is moved to the Upper. Otherwise the content is moved to another
+|* SectionFrame, which has to be potentially merged.
+|*/
+// If a multi-column section is cancelled, the ContentFrames have to be
+// invalidated
+static void lcl_InvalidateInfFlags( SwFrame* pFrame, bool bInva )
+{
+ while ( pFrame )
+ {
+ pFrame->InvalidateInfFlags();
+ if( bInva )
+ {
+ pFrame->InvalidatePos_();
+ pFrame->InvalidateSize_();
+ pFrame->InvalidatePrt_();
+ }
+ if( pFrame->IsLayoutFrame() )
+ lcl_InvalidateInfFlags( static_cast<SwLayoutFrame*>(pFrame)->GetLower(), false );
+ pFrame = pFrame->GetNext();
+ }
+}
+
+// Works like SwContentFrame::ImplGetNextContentFrame, but starts with a LayoutFrame
+static SwContentFrame* lcl_GetNextContentFrame( const SwLayoutFrame* pLay, bool bFwd )
+{
+ if ( bFwd )
+ {
+ if ( pLay->GetNext() && pLay->GetNext()->IsContentFrame() )
+ return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pLay->GetNext()));
+ }
+ else
+ {
+ if ( pLay->GetPrev() && pLay->GetPrev()->IsContentFrame() )
+ return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pLay->GetPrev()));
+ }
+
+ const SwFrame* pFrame = pLay;
+ SwContentFrame *pContentFrame = nullptr;
+ bool bGoingUp = true;
+ do {
+ const SwFrame *p = nullptr;
+ bool bGoingFwdOrBwd = false;
+
+ bool bGoingDown = !bGoingUp && pFrame->IsLayoutFrame();
+ if (bGoingDown)
+ {
+ p = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
+ bGoingDown = nullptr != p;
+ }
+ if ( !bGoingDown )
+ {
+ p = pFrame->IsFlyFrame() ?
+ ( bFwd ? static_cast<const SwFlyFrame*>(pFrame)->GetNextLink() : static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink() ) :
+ ( bFwd ? pFrame->GetNext() :pFrame->GetPrev() );
+ bGoingFwdOrBwd = nullptr != p;
+ if ( !bGoingFwdOrBwd )
+ {
+ p = pFrame->GetUpper();
+ bGoingUp = nullptr != p;
+ if ( !bGoingUp )
+ return nullptr;
+ }
+ }
+
+ bGoingUp = !( bGoingFwdOrBwd || bGoingDown );
+ assert(p);
+ if (!bFwd && bGoingDown)
+ while ( p->GetNext() )
+ p = p->GetNext();
+
+ pFrame = p;
+ } while ( nullptr == (pContentFrame = (pFrame->IsContentFrame() ? const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pFrame)) : nullptr) ));
+
+ return pContentFrame;
+}
+
+namespace
+{
+ SwLayoutFrame* FirstLeaf(SwSectionFrame* pLayFrame)
+ {
+ if (pLayFrame->Lower() && pLayFrame->Lower()->IsColumnFrame())
+ return pLayFrame->GetNextLayoutLeaf();
+ return pLayFrame;
+ }
+
+ /// Checks if pFrame has a parent that can contain a split section frame.
+ bool CanContainSplitSection(const SwFrame* pFrame)
+ {
+ if (!pFrame->IsInTab())
+ return true;
+
+ // The frame is in a table, see if the table is in a section.
+ bool bRet = !pFrame->FindTabFrame()->IsInSct();
+
+ if (bRet)
+ {
+ // Don't try to split if the frame itself is a section frame with
+ // multiple columns.
+ if (pFrame->IsSctFrame())
+ {
+ const SwFrame* pLower = pFrame->GetLower();
+ if (pLower && pLower->IsColumnFrame())
+ bRet = false;
+ }
+ }
+
+ return bRet;
+ }
+}
+
+void SwSectionFrame::MoveContentAndDelete( SwSectionFrame* pDel, bool bSave )
+{
+ bool bSize = pDel->Lower() && pDel->Lower()->IsColumnFrame();
+ SwFrame* pPrv = pDel->GetPrev();
+ SwLayoutFrame* pUp = pDel->GetUpper();
+ // OD 27.03.2003 #i12711# - initialize local pointer variables.
+ SwSectionFrame* pPrvSct = nullptr;
+ SwSectionFrame* pNxtSct = nullptr;
+ SwSectionFormat* pParent = static_cast<SwSectionFormat*>(pDel->GetFormat())->GetParent();
+ if( pDel->IsInTab() && pParent )
+ {
+ SwTabFrame *pTab = pDel->FindTabFrame();
+ // If we are within a table, we can only have broken up sections that
+ // are inside as well, but not a section that contains the whole table.
+ if( pTab->IsInSct() && pParent == pTab->FindSctFrame()->GetFormat() )
+ pParent = nullptr;
+ }
+ // If our Format has a parent, we have probably broken up another
+ // SectionFrame, which has to be checked. To do so we first acquire the
+ // succeeding and the preceding ContentFrame, let's see if they
+ // lay in the SectionFrames.
+ // OD 27.03.2003 #i12711# - check, if previous and next section belonging
+ // together and can be joined, *not* only if deleted section contains content.
+ if ( pParent )
+ {
+ SwFrame* pPrvContent = lcl_GetNextContentFrame( pDel, false );
+ pPrvSct = pPrvContent ? pPrvContent->FindSctFrame() : nullptr;
+ SwFrame* pNxtContent = lcl_GetNextContentFrame( pDel, true );
+ pNxtSct = pNxtContent ? pNxtContent->FindSctFrame() : nullptr;
+ }
+ else
+ {
+ pParent = nullptr;
+ pPrvSct = pNxtSct = nullptr;
+ }
+
+ // Now the content is put aside and the frame is destroyed
+ SwFrame *pSave = bSave ? ::SaveContent( pDel ) : nullptr;
+ bool bOldFootnote = true;
+ if( pSave && pUp->IsFootnoteFrame() )
+ {
+ bOldFootnote = static_cast<SwFootnoteFrame*>(pUp)->IsColLocked();
+ static_cast<SwFootnoteFrame*>(pUp)->ColLock();
+ }
+ pDel->DelEmpty( true );
+ SwFrame::DestroyFrame(pDel);
+ if( pParent )
+ { // Search for the appropriate insert position
+ if( pNxtSct && pNxtSct->GetFormat() == pParent )
+ { // Here we can insert ourselves at the beginning
+ pUp = FirstLeaf( pNxtSct );
+ pPrv = nullptr;
+ if( pPrvSct && ( pPrvSct->GetFormat() != pParent ) )
+ pPrvSct = nullptr; // In order that nothing is merged
+ }
+ else if( pPrvSct && pPrvSct->GetFormat() == pParent )
+ { // Wonderful, here we can insert ourselves at the end
+ pUp = pPrvSct;
+ if( pUp->Lower() && pUp->Lower()->IsColumnFrame() )
+ {
+ pUp = static_cast<SwLayoutFrame*>(pUp->GetLastLower());
+ // The body of the last column
+ pUp = static_cast<SwLayoutFrame*>(pUp->Lower());
+ }
+ // In order to perform the insertion after the last one
+ pPrv = pUp->GetLastLower();
+ pPrvSct = nullptr; // Such that nothing is merged
+ }
+ else
+ {
+ if( pSave )
+ { // Following situations: before and after the section-to-be
+ // deleted there is the section boundary of the enclosing
+ // section, or another (sibling) section connects subsequently,
+ // that derives from the same Parent.
+ // In that case, there's not (yet) a part of our parent available
+ // that can store the content, so we create it here.
+ pPrvSct = new SwSectionFrame( *pParent->GetSection(), pUp );
+ pPrvSct->InsertBehind( pUp, pPrv );
+ pPrvSct->Init();
+ SwRectFnSet aRectFnSet(pUp);
+ aRectFnSet.MakePos( *pPrvSct, pUp, pPrv, true );
+ pUp = FirstLeaf( pPrvSct );
+ pPrv = nullptr;
+ }
+ pPrvSct = nullptr; // Such that nothing will be merged
+ }
+ }
+ // The content is going to be inserted...
+ if( pSave )
+ {
+ lcl_InvalidateInfFlags( pSave, bSize );
+ ::RestoreContent( pSave, pUp, pPrv );
+ pUp->FindPageFrame()->InvalidateContent();
+ if( !bOldFootnote )
+ static_cast<SwFootnoteFrame*>(pUp)->ColUnlock();
+ }
+ // Now two parts of the superior section could possibly be merged
+ if( pPrvSct && !pPrvSct->IsJoinLocked() )
+ {
+ OSL_ENSURE( pNxtSct, "MoveContent: No Merge" );
+ pPrvSct->MergeNext( pNxtSct );
+ }
+}
+
+void SwSectionFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ if ( IsJoinLocked() || IsColLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
+ return;
+ if( !m_pSection ) // Via DelEmpty
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( getRootFrame()->IsInDelList( this ), "SectionFrame without Section" );
+#endif
+ if( !isFrameAreaPositionValid() )
+ {
+ if( GetUpper() )
+ {
+ SwRectFnSet aRectFnSet(GetUpper());
+ aRectFnSet.MakePos( *this, GetUpper(), GetPrev(), false );
+ }
+
+ if (getFrameArea().Height() == 0)
+ {
+ // SwLayoutFrame::MakeAll() is not called for to-be-deleted
+ // section frames (which would invalidate the position of the
+ // next frame via the SwLayNotify dtor), so call it manually.
+ if (SwFrame* pNext = GetNext())
+ pNext->InvalidatePos();
+ }
+ }
+
+ setFrameAreaPositionValid(true);
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ return;
+ }
+ LockJoin(); // I don't let myself to be destroyed on the way
+
+ while( GetNext() && GetNext() == GetFollow() )
+ {
+ const SwFrame* pFoll = GetFollow();
+ MergeNext( static_cast<SwSectionFrame*>(GetNext()) );
+ if( pFoll == GetFollow() )
+ break;
+ }
+
+ // OD 2004-03-15 #116561# - In online layout join the follows, if section
+ // can grow.
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+
+ // Split sections inside table cells: need to merge all follows of the
+ // section here, as later we won't attempt doing so.
+ bool bCanContainSplitSection = false;
+ if (IsInTab() && GetUpper())
+ bCanContainSplitSection = CanContainSplitSection(GetUpper());
+
+ if( pSh && (pSh->GetViewOptions()->getBrowseMode() || bCanContainSplitSection) &&
+ ( Grow( LONG_MAX, true ) > 0 ) )
+ {
+ while( GetFollow() )
+ {
+ const SwFrame* pFoll = GetFollow();
+ MergeNext( GetFollow() );
+ if( pFoll == GetFollow() )
+ break;
+ }
+ }
+
+ // A section with Follow uses all the space until the lower edge of the
+ // Upper. If it moves, its size can grow or decrease...
+ if( !isFrameAreaPositionValid() && ToMaximize( false ) )
+ {
+ setFrameAreaSizeValid(false);
+ }
+
+ SwLayoutFrame::MakeAll(getRootFrame()->GetCurrShell()->GetOut());
+
+ if (IsInTab())
+ {
+ // In case the section is in a table, then calculate the lower right
+ // now. Just setting the valid size flag of the lower to false may not
+ // be enough, as lcl_RecalcRow() can call
+ // SwFrame::ValidateThisAndAllLowers(), and then we don't attempt
+ // calculating the proper position of the lower.
+ SwFrame* pLower = Lower();
+ if (pLower && !pLower->isFrameAreaPositionValid())
+ pLower->Calc(pRenderContext);
+ }
+
+ UnlockJoin();
+ if( m_pSection && IsSuperfluous() )
+ DelEmpty( false );
+}
+
+bool SwSectionFrame::ShouldBwdMoved( SwLayoutFrame *, bool & )
+{
+ OSL_FAIL( "Oops, where is my tinfoil hat?" );
+ return false;
+}
+
+const SwSectionFormat* SwSectionFrame::GetEndSectFormat_() const
+{
+ const SwSectionFormat *pFormat = m_pSection->GetFormat();
+ while( !pFormat->GetEndAtTextEnd().IsAtEnd() )
+ {
+ if( auto pNewFormat = dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) )
+ pFormat = pNewFormat;
+ else
+ return nullptr;
+ }
+ return pFormat;
+}
+
+static void lcl_FindContentFrame( SwContentFrame* &rpContentFrame, SwFootnoteFrame* &rpFootnoteFrame,
+ SwFrame* pFrame, bool &rbChkFootnote )
+{
+ if( !pFrame )
+ return;
+
+ while( pFrame->GetNext() )
+ pFrame = pFrame->GetNext();
+ while( !rpContentFrame && pFrame )
+ {
+ if( pFrame->IsContentFrame() )
+ rpContentFrame = static_cast<SwContentFrame*>(pFrame);
+ else if( pFrame->IsLayoutFrame() )
+ {
+ if( pFrame->IsFootnoteFrame() )
+ {
+ if( rbChkFootnote )
+ {
+ rpFootnoteFrame = static_cast<SwFootnoteFrame*>(pFrame);
+ rbChkFootnote = rpFootnoteFrame->GetAttr()->GetFootnote().IsEndNote();
+ }
+ }
+ else
+ lcl_FindContentFrame( rpContentFrame, rpFootnoteFrame,
+ static_cast<SwLayoutFrame*>(pFrame)->Lower(), rbChkFootnote );
+ }
+ pFrame = pFrame->GetPrev();
+ }
+}
+
+SwContentFrame *SwSectionFrame::FindLastContent( SwFindMode nMode )
+{
+ SwContentFrame *pRet = nullptr;
+ SwFootnoteFrame *pFootnoteFrame = nullptr;
+ SwSectionFrame *pSect = this;
+ if( nMode != SwFindMode::None )
+ {
+ const SwSectionFormat *pFormat = IsEndnAtEnd() ? GetEndSectFormat() :
+ m_pSection->GetFormat();
+ do {
+ while( pSect->HasFollow() )
+ pSect = pSect->GetFollow();
+ SwFrame* pTmp = pSect->FindNext();
+ while( pTmp && pTmp->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pTmp)->GetSection() )
+ pTmp = pTmp->FindNext();
+ if( pTmp && pTmp->IsSctFrame() &&
+ static_cast<SwSectionFrame*>(pTmp)->IsDescendantFrom( pFormat ) )
+ pSect = static_cast<SwSectionFrame*>(pTmp);
+ else
+ break;
+ } while( true );
+ }
+ bool bFootnoteFound = nMode == SwFindMode::EndNote;
+ do
+ {
+ lcl_FindContentFrame( pRet, pFootnoteFrame, pSect->Lower(), bFootnoteFound );
+ if( pRet || !pSect->IsFollow() || nMode == SwFindMode::None ||
+ ( SwFindMode::MyLast == nMode && this == pSect ) )
+ break;
+ pSect = pSect->FindMaster();
+ } while( pSect );
+ if( ( nMode == SwFindMode::EndNote ) && pFootnoteFrame )
+ pRet = pFootnoteFrame->ContainsContent();
+ return pRet;
+}
+
+bool SwSectionFrame::CalcMinDiff( SwTwips& rMinDiff ) const
+{
+ if( ToMaximize( true ) )
+ {
+ SwRectFnSet aRectFnSet(this);
+ rMinDiff = aRectFnSet.GetPrtBottom(*GetUpper());
+ rMinDiff = aRectFnSet.BottomDist( getFrameArea(), rMinDiff );
+ return true;
+ }
+ return false;
+}
+
+/**
+ * CollectEndnotes looks for endnotes in the sectionfrm and his follows,
+ * the endnotes will cut off the layout and put into the array.
+ * If the first endnote is not a master-SwFootnoteFrame, the whole sectionfrm
+ * contains only endnotes and it is not necessary to collect them.
+ */
+static SwFootnoteFrame* lcl_FindEndnote( SwSectionFrame* &rpSect, bool &rbEmpty,
+ SwLayouter *pLayouter )
+{
+ // if rEmpty is set, the rpSect is already searched
+ SwSectionFrame* pSect = rbEmpty ? rpSect->GetFollow() : rpSect;
+ while( pSect )
+ {
+ OSL_ENSURE( (pSect->Lower() && pSect->Lower()->IsColumnFrame()) || pSect->GetUpper()->IsFootnoteFrame(),
+ "InsertEndnotes: Where's my column?" );
+
+ // i73332: Columned section in endnote
+ SwColumnFrame* pCol = nullptr;
+ if(pSect->Lower() && pSect->Lower()->IsColumnFrame())
+ pCol = static_cast<SwColumnFrame*>(pSect->Lower());
+
+ while( pCol ) // check all columns
+ {
+ SwFootnoteContFrame* pFootnoteCont = pCol->FindFootnoteCont();
+ if( pFootnoteCont )
+ {
+ SwFootnoteFrame* pRet = static_cast<SwFootnoteFrame*>(pFootnoteCont->Lower());
+ while( pRet ) // look for endnotes
+ {
+ /* CollectEndNode can destroy pRet so we need to get the
+ next early
+ */
+ SwFootnoteFrame* pRetNext = static_cast<SwFootnoteFrame*>(pRet->GetNext());
+ if( pRet->GetAttr()->GetFootnote().IsEndNote() )
+ {
+ if( pRet->GetMaster() )
+ {
+ if( pLayouter )
+ pLayouter->CollectEndnote( pRet );
+ else
+ return nullptr;
+ }
+ else
+ return pRet; // Found
+ }
+ pRet = pRetNext;
+ }
+ }
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ }
+ rpSect = pSect;
+ pSect = pLayouter ? pSect->GetFollow() : nullptr;
+ rbEmpty = true;
+ }
+ return nullptr;
+}
+
+static void lcl_ColumnRefresh( SwSectionFrame* pSect, bool bFollow )
+{
+ vcl::RenderContext* pRenderContext = pSect->getRootFrame()->GetCurrShell()->GetOut();
+ while( pSect )
+ {
+ bool bOldLock = pSect->IsColLocked();
+ pSect->ColLock();
+ if( pSect->Lower() && pSect->Lower()->IsColumnFrame() )
+ {
+ SwColumnFrame *pCol = static_cast<SwColumnFrame*>(pSect->Lower());
+ do
+ { pCol->InvalidateSize_();
+ pCol->InvalidatePos_();
+ static_cast<SwLayoutFrame*>(pCol)->Lower()->InvalidateSize_();
+ pCol->Calc(pRenderContext); // calculation of column and
+ static_cast<SwLayoutFrame*>(pCol)->Lower()->Calc(pRenderContext); // body
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ } while ( pCol );
+ }
+ if( !bOldLock )
+ pSect->ColUnlock();
+ if( bFollow )
+ pSect = pSect->GetFollow();
+ else
+ pSect = nullptr;
+ }
+}
+
+void SwSectionFrame::CollectEndnotes( SwLayouter* pLayouter )
+{
+ OSL_ENSURE( IsColLocked(), "CollectEndnotes: You love the risk?" );
+ // i73332: Section in footnode does not have columns!
+ OSL_ENSURE( (Lower() && Lower()->IsColumnFrame()) || GetUpper()->IsFootnoteFrame(), "Where's my column?" );
+
+ SwSectionFrame* pSect = this;
+ SwFootnoteFrame* pFootnote;
+ bool bEmpty = false;
+ // pSect is the last sectionfrm without endnotes or the this-pointer
+ // the first sectionfrm with endnotes may be destroyed, when the endnotes
+ // is cutted
+ while( nullptr != (pFootnote = lcl_FindEndnote( pSect, bEmpty, pLayouter )) )
+ pLayouter->CollectEndnote( pFootnote );
+ if( pLayouter->HasEndnotes() )
+ lcl_ColumnRefresh( this, true );
+}
+
+/** Fits the size to the surroundings.
+|*
+|* Those that have a Follow or foot notes, have to extend until
+|* the lower edge of a upper (bMaximize)
+|* They must not extend above the Upper, as the case may be one can
+|* try to grow its upper (bGrow)
+|* If the size had to be changed, the content is calculated.
+|*
+|* @note: perform calculation of content, only if height has changed (OD 18.09.2002 #100522#)
+|*/
+void SwSectionFrame::CheckClipping( bool bGrow, bool bMaximize )
+{
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nDiff;
+ SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
+ if( bGrow && ( !IsInFly() || !GetUpper()->IsColBodyFrame() ||
+ !FindFlyFrame()->IsLocked() ) )
+ {
+ nDiff = -aRectFnSet.BottomDist( getFrameArea(), nDeadLine );
+ if( !bMaximize )
+ nDiff += Undersize();
+ if( nDiff > 0 )
+ {
+ tools::Long nAdd = GetUpper()->Grow( nDiff );
+ if( aRectFnSet.IsVert() )
+ nDeadLine -= nAdd;
+ else
+ nDeadLine += nAdd;
+ }
+ }
+ nDiff = -aRectFnSet.BottomDist( getFrameArea(), nDeadLine );
+ SetUndersized( !bMaximize && nDiff >= 0 );
+ const bool bCalc = ( IsUndersized() || bMaximize ) &&
+ ( nDiff ||
+ aRectFnSet.GetTop(getFramePrintArea()) > aRectFnSet.GetHeight(getFrameArea()) );
+ // OD 03.11.2003 #i19737# - introduce local variable <bExtraCalc> to indicate
+ // that a calculation has to be done beside the value of <bCalc>.
+ bool bExtraCalc = false;
+ if( !bCalc && !bGrow && IsAnyNoteAtEnd() && !IsInFootnote() )
+ {
+ SwSectionFrame *pSect = this;
+ bool bEmpty = false;
+ SwLayoutFrame* pFootnote = IsEndnAtEnd() ?
+ lcl_FindEndnote( pSect, bEmpty, nullptr ) : nullptr;
+ if( pFootnote )
+ {
+ pFootnote = pFootnote->FindFootnoteBossFrame();
+ SwFrame* pTmp = FindLastContent( SwFindMode::LastCnt );
+ // OD 08.11.2002 #104840# - use <SwLayoutFrame::IsBefore(..)>
+ if ( pTmp && pFootnote->IsBefore( pTmp->FindFootnoteBossFrame() ) )
+ bExtraCalc = true;
+ }
+ else if( GetFollow() && !GetFollow()->ContainsAny() )
+ bExtraCalc = true;
+ }
+ if ( !(bCalc || bExtraCalc) )
+ return;
+
+ nDiff = aRectFnSet.YDiff( nDeadLine, aRectFnSet.GetTop(getFrameArea()) );
+ if( nDiff < 0 )
+ nDeadLine = aRectFnSet.GetTop(getFrameArea());
+ const Size aOldSz( getFramePrintArea().SSize() );
+ tools::Long nTop = aRectFnSet.GetTopMargin(*this);
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetBottom( aFrm, nDeadLine );
+ }
+
+ nDiff = aRectFnSet.GetHeight(getFrameArea());
+ if( nTop > nDiff )
+ nTop = nDiff;
+ aRectFnSet.SetYMargins( *this, nTop, 0 );
+
+ // OD 18.09.2002 #100522#
+ // Determine, if height has changed.
+ // Note: In vertical layout the height equals the width value.
+ bool bHeightChanged = aRectFnSet.IsVert() ?
+ (aOldSz.Width() != getFramePrintArea().Width()) :
+ (aOldSz.Height() != getFramePrintArea().Height());
+ // Last but not least we have changed the height again, thus the inner
+ // layout (columns) is calculated and the content as well.
+ // OD 18.09.2002 #100522#
+ // calculate content, only if height has changed.
+ // OD 03.11.2003 #i19737# - restriction of content calculation too strong.
+ // If an endnote has an incorrect position or a follow section contains
+ // no content except footnotes/endnotes, the content has also been calculated.
+ if ( !(( bHeightChanged || bExtraCalc ) && Lower()) )
+ return;
+
+ if( Lower()->IsColumnFrame() )
+ {
+ lcl_ColumnRefresh( this, false );
+ ::CalcContent( this );
+ }
+ else
+ {
+ ChgLowersProp( aOldSz );
+ if( !bMaximize && !IsContentLocked() )
+ ::CalcContent( this );
+ }
+}
+
+void SwSectionFrame::SimpleFormat()
+{
+ if ( IsJoinLocked() || IsColLocked() )
+ return;
+ LockJoin();
+ SwRectFnSet aRectFnSet(this);
+ if( GetPrev() || GetUpper() )
+ {
+ // assure notifications on position changes.
+ const SwLayNotify aNotify( this );
+ aRectFnSet.MakePos( *this, GetUpper(), GetPrev(), false );
+ setFrameAreaPositionValid(true);
+ }
+ SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
+ // OD 22.10.2002 #97265# - call always method <lcl_ColumnRefresh(..)>, in
+ // order to get calculated lowers, not only if there space left in its upper.
+ if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) >= 0 )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetBottom( aFrm, nDeadLine );
+ }
+
+ tools::Long nHeight = aRectFnSet.GetHeight(getFrameArea());
+ tools::Long nTop = CalcUpperSpace();
+ if( nTop > nHeight )
+ nTop = nHeight;
+ aRectFnSet.SetYMargins( *this, nTop, 0 );
+ }
+ lcl_ColumnRefresh( this, false );
+ UnlockJoin();
+}
+
+namespace {
+
+// #i40147# - helper class to perform extra section format
+// to position anchored objects and to keep the position of whose objects locked.
+class ExtraFormatToPositionObjs
+{
+ private:
+ SwSectionFrame* mpSectFrame;
+ bool mbExtraFormatPerformed;
+
+ public:
+ explicit ExtraFormatToPositionObjs( SwSectionFrame& _rSectFrame)
+ : mpSectFrame( &_rSectFrame ),
+ mbExtraFormatPerformed( false )
+ {}
+
+ ~ExtraFormatToPositionObjs()
+ {
+ if ( !mbExtraFormatPerformed )
+ return;
+
+ // release keep locked position of lower floating screen objects
+ SwPageFrame* pPageFrame = mpSectFrame->FindPageFrame();
+ SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( mpSectFrame->IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
+ {
+ pAnchoredObj->SetKeepPosLocked( false );
+ }
+ }
+ }
+ }
+
+ // #i81555#
+ void InitObjs( SwFrame& rFrame )
+ {
+ SwSortedObjs* pObjs = rFrame.GetDrawObjs();
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ pAnchoredObj->UnlockPosition();
+ pAnchoredObj->SetClearedEnvironment( false );
+ }
+ }
+ if ( rFrame.IsLayoutFrame() )
+ {
+ SwFrame* pLowerFrame = rFrame.GetLower();
+ while ( pLowerFrame != nullptr )
+ {
+ InitObjs( *pLowerFrame );
+
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+ }
+ }
+
+ void FormatSectionToPositionObjs()
+ {
+ vcl::RenderContext* pRenderContext = mpSectFrame->getRootFrame()->GetCurrShell()->GetOut();
+ // perform extra format for multi-columned section.
+ if ( !(mpSectFrame->Lower() && mpSectFrame->Lower()->IsColumnFrame() &&
+ mpSectFrame->Lower()->GetNext()) )
+ return;
+
+ // grow section till bottom of printing area of upper frame
+ SwRectFnSet aRectFnSet(mpSectFrame);
+ SwTwips nTopMargin = aRectFnSet.GetTopMargin(*mpSectFrame);
+ Size aOldSectPrtSize( mpSectFrame->getFramePrintArea().SSize() );
+ SwTwips nDiff = aRectFnSet.BottomDist( mpSectFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*mpSectFrame->GetUpper()) );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mpSectFrame);
+ aRectFnSet.AddBottom( aFrm, nDiff );
+ }
+
+ aRectFnSet.SetYMargins( *mpSectFrame, nTopMargin, 0 );
+ // #i59789#
+ // suppress formatting, if printing area of section is too narrow
+ if ( aRectFnSet.GetHeight(mpSectFrame->getFramePrintArea()) <= 0 )
+ {
+ return;
+ }
+ mpSectFrame->ChgLowersProp( aOldSectPrtSize );
+
+ // format column frames and its body and footnote container
+ SwColumnFrame* pColFrame = static_cast<SwColumnFrame*>(mpSectFrame->Lower());
+ while ( pColFrame )
+ {
+ pColFrame->Calc(pRenderContext);
+ pColFrame->Lower()->Calc(pRenderContext);
+ if ( pColFrame->Lower()->GetNext() )
+ {
+ pColFrame->Lower()->GetNext()->Calc(pRenderContext);
+ }
+
+ pColFrame = static_cast<SwColumnFrame*>(pColFrame->GetNext());
+ }
+
+ // unlock position of lower floating screen objects for the extra format
+ // #i81555#
+ // Section frame can already have changed the page and its content
+ // can still be on the former page.
+ // Thus, initialize objects via lower-relationship
+ InitObjs( *mpSectFrame );
+
+ // format content - first with collecting its foot-/endnotes before content
+ // format, second without collecting its foot-/endnotes.
+ ::CalcContent( mpSectFrame );
+ ::CalcContent( mpSectFrame, true );
+
+ // keep locked position of lower floating screen objects
+ SwPageFrame* pPageFrame = mpSectFrame->FindPageFrame();
+ SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( mpSectFrame->IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
+ {
+ pAnchoredObj->SetKeepPosLocked( true );
+ }
+ }
+ }
+
+ mbExtraFormatPerformed = true;
+
+ }
+};
+
+}
+
+/// "formats" the frame; Frame and PrtArea
+void SwSectionFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttr )
+{
+ if( !m_pSection ) // via DelEmpty
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( getRootFrame()->IsInDelList( this ), "SectionFrame without Section" );
+#endif
+ setFrameAreaPositionValid(true);
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ return;
+ }
+
+ SwRectFnSet aRectFnSet(this);
+
+ if ( !isFramePrintAreaValid() )
+ {
+ PROTOCOL( this, PROT::PrintArea, DbgAction::NONE, nullptr )
+ setFramePrintAreaValid(true);
+ SwTwips nUpper = CalcUpperSpace();
+
+ // #109700# LRSpace for sections
+ const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
+ aRectFnSet.SetXMargins( *this, rLRSpace.GetLeft(), rLRSpace.GetRight() );
+
+ if( nUpper != aRectFnSet.GetTopMargin(*this) )
+ {
+ setFrameAreaSizeValid(false);
+ SwFrame* pOwn = ContainsAny();
+ if( pOwn )
+ pOwn->InvalidatePos_();
+ }
+ aRectFnSet.SetYMargins( *this, nUpper, 0 );
+ }
+
+ if ( isFrameAreaSizeValid() )
+ return;
+
+ PROTOCOL_ENTER( this, PROT::Size, DbgAction::NONE, nullptr )
+ const tools::Long nOldHeight = aRectFnSet.GetHeight(getFrameArea());
+ bool bOldLock = IsColLocked();
+ ColLock();
+
+ setFrameAreaSizeValid(true);
+
+ // The size is only determined by the content, if the SectFrame does not have a
+ // Follow. Otherwise it fills (occupies) the Upper down to the lower edge.
+ // It is not responsible for the text flow, but the content is.
+ bool bMaximize = ToMaximize( false );
+
+ // OD 2004-05-17 #i28701# - If the wrapping style has to be considered
+ // on object positioning, an extra formatting has to be performed
+ // to determine the correct positions the floating screen objects.
+ // #i40147#
+ // use new helper class <ExtraFormatToPositionObjs>.
+ // This class additionally keep the locked position of the objects
+ // and releases this position lock keeping on destruction.
+ ExtraFormatToPositionObjs aExtraFormatToPosObjs( *this );
+ if ( !bMaximize &&
+ GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) &&
+ !GetFormat()->GetBalancedColumns().GetValue() )
+ {
+ aExtraFormatToPosObjs.FormatSectionToPositionObjs();
+ }
+
+ // Column widths have to be adjusted before calling CheckClipping.
+ // CheckClipping can cause the formatting of the lower frames
+ // which still have a width of 0.
+ const bool bHasColumns = Lower() && Lower()->IsColumnFrame();
+ if ( bHasColumns && Lower()->GetNext() )
+ AdjustColumns( nullptr, false );
+
+ if( GetUpper() )
+ {
+ const tools::Long nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetWidth( aFrm, nWidth );
+ }
+
+ // #109700# LRSpace for sections
+ {
+ const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetWidth( aPrt, nWidth - rLRSpace.GetLeft() - rLRSpace.GetRight() );
+ }
+
+ // OD 15.10.2002 #103517# - allow grow in online layout
+ // Thus, set <..IsBrowseMode()> as parameter <bGrow> on calling
+ // method <CheckClipping(..)>.
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ CheckClipping( pSh && pSh->GetViewOptions()->getBrowseMode(), bMaximize );
+ bMaximize = ToMaximize( false );
+ setFrameAreaSizeValid(true);
+ }
+
+ // Check the width of the columns and adjust if necessary
+ if ( bHasColumns && ! Lower()->GetNext() && bMaximize )
+ static_cast<SwColumnFrame*>(Lower())->Lower()->Calc(pRenderContext);
+
+ if ( !bMaximize )
+ {
+ SwTwips nRemaining = aRectFnSet.GetTopMargin(*this);
+ SwFrame *pFrame = m_pLower;
+ if( pFrame )
+ {
+ if( pFrame->IsColumnFrame() && pFrame->GetNext() )
+ {
+ // #i61435#
+ // suppress formatting, if upper frame has height <= 0
+ if ( aRectFnSet.GetHeight(GetUpper()->getFrameArea()) > 0 )
+ {
+ FormatWidthCols( *pAttr, nRemaining, MINLAY );
+ }
+ // #126020# - adjust check for empty section
+ // #130797# - correct fix #126020#
+ while( HasFollow() && !GetFollow()->ContainsContent() &&
+ !GetFollow()->ContainsAny( true ) )
+ {
+ SwFrame* pOld = GetFollow();
+ GetFollow()->DelEmpty( false );
+ if( pOld == GetFollow() )
+ break;
+ }
+ bMaximize = ToMaximize( false );
+ nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ }
+ else
+ {
+ if( pFrame->IsColumnFrame() )
+ {
+ pFrame->Calc(pRenderContext);
+ pFrame = static_cast<SwColumnFrame*>(pFrame)->Lower();
+ pFrame->Calc(pRenderContext);
+ pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower();
+ CalcFootnoteContent();
+ }
+ // If we are in a columned frame which calls a CalcContent
+ // in the FormatWidthCols, the content might need calculating
+ if( pFrame && !pFrame->isFrameAreaDefinitionValid() && IsInFly() &&
+ FindFlyFrame()->IsColLocked() )
+ ::CalcContent( this );
+ nRemaining += InnerHeight();
+ bMaximize = HasFollow();
+ }
+ }
+
+ SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
+ if( nDiff < 0)
+ {
+ SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
+ {
+ tools::Long nBottom = aRectFnSet.GetBottom(getFrameArea());
+ nBottom = aRectFnSet.YInc( nBottom, -nDiff );
+ tools::Long nTmpDiff = aRectFnSet.YDiff( nBottom, nDeadLine );
+ if( nTmpDiff > 0 )
+ {
+ nTmpDiff = GetUpper()->Grow( nTmpDiff, true );
+ nDeadLine = aRectFnSet.YInc( nDeadLine, nTmpDiff );
+ nTmpDiff = aRectFnSet.YDiff( nBottom, nDeadLine );
+ if( nTmpDiff > 0 )
+ nDiff += nTmpDiff;
+ if( nDiff > 0 )
+ nDiff = 0;
+ }
+ }
+ }
+ if( nDiff )
+ {
+ tools::Long nTmp = nRemaining - aRectFnSet.GetHeight(getFrameArea());
+ tools::Long nTop = aRectFnSet.GetTopMargin(*this);
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nTmp );
+ }
+
+ aRectFnSet.SetYMargins( *this, nTop, 0 );
+ InvalidateNextPos();
+
+ if (m_pLower && (!m_pLower->IsColumnFrame() || !m_pLower->GetNext()))
+ {
+ // If a single-column section just created the space that
+ // was requested by the "undersized" paragraphs, then they
+ // have to be invalidated and calculated, so they fully cover it
+ pFrame = m_pLower;
+ if( pFrame->IsColumnFrame() )
+ {
+ pFrame->InvalidateSize_();
+ pFrame->InvalidatePos_();
+ pFrame->Calc(pRenderContext);
+ pFrame = static_cast<SwColumnFrame*>(pFrame)->Lower();
+ pFrame->Calc(pRenderContext);
+ pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower();
+ CalcFootnoteContent();
+ }
+ bool bUnderSz = false;
+ while( pFrame )
+ {
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ {
+ pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ bUnderSz = true;
+ }
+ pFrame = pFrame->GetNext();
+ }
+ if( bUnderSz && !IsContentLocked() )
+ ::CalcContent( this );
+ }
+ }
+ }
+
+ // Do not exceed the lower edge of the Upper.
+ // Do not extend below the lower edge with Sections with Follows
+ if ( GetUpper() )
+ CheckClipping( true, bMaximize );
+ if( !bOldLock )
+ ColUnlock();
+ tools::Long nDiff = nOldHeight - aRectFnSet.GetHeight(getFrameArea());
+
+ if( nDiff > 0 )
+ {
+ if( !GetNext() )
+ SetRetouche(); // Take over the retouching ourselves
+ if( GetUpper() && !GetUpper()->IsFooterFrame() )
+ GetUpper()->Shrink( nDiff );
+ }
+
+ if( IsUndersized() )
+ {
+ setFramePrintAreaValid(true);
+ }
+
+}
+
+/// Returns the next layout sheet where the frame can be moved in.
+/// New pages are created only if specified by the parameter.
+SwLayoutFrame *SwFrame::GetNextSctLeaf( MakePageType eMakePage )
+{
+ // Attention: Nested sections are currently not supported
+
+ PROTOCOL_ENTER( this, PROT::Leaf, DbgAction::NextSect, GetUpper()->FindSctFrame() )
+
+ // Shortcuts for "columned" sections, if we're not in the last column
+ // Can we slide to the next column of the section?
+ if( IsColBodyFrame() && GetUpper()->GetNext() )
+ return static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(GetUpper()->GetNext())->Lower());
+ if( GetUpper()->IsColBodyFrame() && GetUpper()->GetUpper()->GetNext() )
+ return static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(GetUpper()->GetUpper()->GetNext())->Lower());
+ // Inside a table-in-section, or sections of headers/footers, there can be only
+ // one column shift be made, one of the above shortcuts should have applied!
+ if( !CanContainSplitSection(GetUpper()) || FindFooterOrHeader() )
+ return nullptr;
+
+ SwSectionFrame *pSect = FindSctFrame();
+ bool bWrongPage = false;
+ assert(pSect && "GetNextSctLeaf: Missing SectionFrame");
+
+ // Shortcut for sections with Follows. That's ok,
+ // if no columns or pages (except dummy pages) lie in between.
+ // In case of linked frames and in footnotes the shortcut would get
+ // even more costly
+ if( pSect->HasFollow() && pSect->IsInDocBody() && !pSect->IsInTab() )
+ {
+ if( pSect->GetFollow() == pSect->GetNext() )
+ {
+ SwPageFrame *pPg = pSect->GetFollow()->FindPageFrame();
+ if( WrongPageDesc( pPg ) )
+ bWrongPage = true;
+ else
+ return FirstLeaf( pSect->GetFollow() );
+ }
+ else
+ {
+ SwFrame* pTmp;
+ if( !pSect->GetUpper()->IsColBodyFrame() ||
+ nullptr == ( pTmp = pSect->GetUpper()->GetUpper()->GetNext() ) )
+ pTmp = pSect->FindPageFrame()->GetNext();
+ if( pTmp ) // is now the next column or page
+ {
+ SwFrame* pTmpX = pTmp;
+ if( pTmp->IsPageFrame() && static_cast<SwPageFrame*>(pTmp)->IsEmptyPage() )
+ pTmp = pTmp->GetNext(); // skip dummy pages
+ SwFrame *pUp = pSect->GetFollow()->GetUpper();
+ // pUp becomes the next column if the Follow lies in a column
+ // that is not a "not first" one, otherwise the page
+ if( !pUp->IsColBodyFrame() ||
+ !( pUp = pUp->GetUpper() )->GetPrev() )
+ pUp = pUp->FindPageFrame();
+ // Now pUp and pTmp have to be the same page/column, otherwise
+ // pages or columns lie between Master and Follow
+ if( pUp == pTmp || pUp->GetNext() == pTmpX )
+ {
+ SwPageFrame* pNxtPg = pUp->IsPageFrame() ?
+ static_cast<SwPageFrame*>(pUp) : pUp->FindPageFrame();
+ if( WrongPageDesc( pNxtPg ) )
+ bWrongPage = true;
+ else
+ return FirstLeaf( pSect->GetFollow() );
+ }
+ }
+ }
+ }
+
+#ifndef NDEBUG
+ std::vector<SwFrame *> parents;
+ for (SwFrame * pTmp = GetUpper(); pTmp && !pTmp->IsPageFrame(); pTmp = pTmp->GetUpper())
+ {
+ parents.push_back(pTmp);
+ }
+#endif
+
+ // Always end up in the same section: Body again inside Body etc.
+ const bool bBody = IsInDocBody();
+ const bool bFootnotePage = FindPageFrame()->IsFootnotePage();
+
+ // The "pLayLeaf is in a table" case is rejected by default, so that it
+ // can't happen that we try to move a table to one of its own cells.
+ bool bLayLeafTableAllowed = false;
+ SwLayoutFrame *pLayLeaf;
+
+ SwLayoutFrame* pCellLeaf = nullptr;
+ if (GetUpper()->IsInTab())
+ {
+ if (IsTabFrame())
+ {
+ return nullptr; // table in section in table: split disabled for now
+ }
+ // We are *in* a table (not an outermost SwTabFrame), see if there
+ // is a follow cell frame created already.
+ pCellLeaf = GetNextCellLeaf();
+ if (!pCellLeaf)
+ {
+ SAL_WARN("sw.layout", "section is in table, but the table is not split");
+ return nullptr;
+ }
+ }
+
+ // A shortcut for TabFrames such that not all cells need to be visited
+ if( bWrongPage )
+ pLayLeaf = nullptr;
+ else if( IsTabFrame() )
+ {
+ SwFrame *const pTmpCnt = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
+ pLayLeaf = pTmpCnt ? pTmpCnt->GetUpper() : nullptr;
+ }
+ else if (pCellLeaf && CanContainSplitSection(this))
+ {
+ // This frame is in a table-not-in-section, its follow should be
+ // inserted under the follow of the frame's cell.
+ pLayLeaf = pCellLeaf;
+ if (pLayLeaf->FindTabFrame() == FindTabFrame())
+ SAL_WARN("sw.layout", "my table frame and my follow's table frame is the same");
+ // In this case pLayLeaf pointing to an in-table frame is OK.
+ bLayLeafTableAllowed = true;
+ }
+ else
+ {
+ pLayLeaf = GetNextLayoutLeaf();
+ if( IsColumnFrame() )
+ {
+ while( pLayLeaf && static_cast<SwColumnFrame*>(this)->IsAnLower( pLayLeaf ) )
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ }
+ }
+
+ SwLayoutFrame *pOldLayLeaf = nullptr; // Such that in case of newly
+ // created pages, the search is
+ // not started over at the beginning
+
+ while( true )
+ {
+ if( pLayLeaf )
+ {
+ // A layout leaf was found, let's see whether it can store me or
+ // another SectionFrame can be inserted here, or we have to continue
+ // searching
+ SwPageFrame* pNxtPg = pLayLeaf->FindPageFrame();
+ if ( !bFootnotePage && pNxtPg->IsFootnotePage() )
+ { // If I reached the end note pages it's over
+ pLayLeaf = nullptr;
+ continue;
+ }
+ // Once inBody always inBody, don't step into tables-in-sections and not into other sections
+ if ( (bBody && !pLayLeaf->IsInDocBody()) ||
+ (IsInFootnote() != pLayLeaf->IsInFootnote() ) ||
+ (pLayLeaf->IsInTab() && !bLayLeafTableAllowed) ||
+ ( pLayLeaf->IsInSct() && ( !pSect->HasFollow()
+ || pSect->GetFollow() != pLayLeaf->FindSctFrame() ) ) )
+ {
+ // Rejected - try again.
+ pOldLayLeaf = pLayLeaf;
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ continue;
+ }
+ // Page desc is never wrong in case of sections-in-tables: in that
+ // case pLayLeaf points to our section's cell's follow, which is
+ // fine to be on the same page. New page creation is handled when
+ // creating / moving the cell frame.
+ if( WrongPageDesc( pNxtPg ) && !bLayLeafTableAllowed )
+ {
+ if( bWrongPage )
+ break; // there's a column between me and my right page
+ pLayLeaf = nullptr;
+ bWrongPage = true;
+ pOldLayLeaf = nullptr;
+ continue;
+ }
+ }
+ // There is no further LayoutFrame that fits, so a new page
+ // has to be created, although new pages are worthless within a frame
+ else if( !pSect->IsInFly() &&
+ ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT ) )
+ {
+ InsertPage(pOldLayLeaf ? pOldLayLeaf->FindPageFrame() : FindPageFrame(),
+ false );
+ // and again the whole thing
+ if (pCellLeaf && CanContainSplitSection(this))
+ // GetNextLayoutLeaf() would refer to the next cell in the same
+ // row, avoid that. pCellLeaf points to the correct cell in the
+ // follow table, and in the next round it'll be used, as we now
+ // have a next page.
+ pLayLeaf = pCellLeaf;
+ else
+ pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf();
+ continue;
+ }
+ break;
+ }
+
+ if( pLayLeaf )
+ {
+ // We have found the suitable layout sheet. If there (in the sheet) is
+ // already a Follow of our section, we take its first layout sheet,
+ // otherwise it is time to create a section follow
+ SwSectionFrame* pNew = nullptr;
+
+ // This can be omitted if existing Follows were cut short
+ SwFrame* pFirst = pLayLeaf->Lower();
+ // Here SectionFrames that are to be deleted must be ignored
+ while( pFirst && pFirst->IsSctFrame() && !static_cast<SwSectionFrame*>(pFirst)->GetSection() )
+ pFirst = pFirst->GetNext();
+ if( pFirst && pFirst->IsSctFrame() && pSect->GetFollow() == pFirst )
+ pNew = pSect->GetFollow();
+ else if( MAKEPAGE_NOSECTION == eMakePage )
+ return pLayLeaf;
+ else if (pSect->GetSection())
+ {
+ pNew = new SwSectionFrame( *pSect, false );
+ pNew->InsertBefore( pLayLeaf, pLayLeaf->Lower() );
+ pNew->Init();
+ SwRectFnSet aRectFnSet(pNew);
+ aRectFnSet.MakePos( *pNew, pLayLeaf, nullptr, true );
+
+#ifndef NDEBUG
+ { // sanity check the parents of the new frame vs. the old frame
+ SwFrame * pTmp = pNew;
+ auto iter(parents.begin());
+ if (parents.size() >= 2 &&
+ parents[0]->IsBodyFrame() && parents[1]->IsColumnFrame())
+ { // this only inserts section frame - remove column
+ assert(parents[2]->IsSctFrame() || IsSctFrame());
+ if (parents[2]->IsSctFrame())
+ std::advance(iter, +2);
+ else
+ pTmp = pTmp->GetUpper();
+ }
+ else if (IsBodyFrame() && parents.size() >= 1
+ && parents[0]->IsColumnFrame())
+ { // same as above, special case: "this" is the body frame
+ assert(parents[1]->IsSctFrame());
+ std::advance(iter, +1);
+ }
+ else if (IsSctFrame()) // special case: "this" is the section
+ {
+ pTmp = pTmp->GetUpper();
+ }
+
+ for ( ; iter != parents.end(); ++iter)
+ {
+ if (pTmp->IsPageFrame())
+ {
+ if ((*iter)->IsColumnFrame() &&
+ (iter + 1) != parents.end() && (*(iter + 1))->IsBodyFrame())
+ { // page style has columns - evidently these are
+ break; // added later?
+ }
+ assert(!pTmp->IsPageFrame());
+ }
+ assert(pTmp->GetType() == (*iter)->GetType());
+ // for cell frames and table frames:
+ // 1) there may be multiple follow frames of the old one
+ // 2) the new frame may be identical to the old one
+ // (not sure if this is allowed, but it happens now
+ // for the outer table of a nested table)
+ if (pTmp->IsCellFrame())
+ {
+ SwCellFrame const*const pNewF(static_cast<SwCellFrame*>(pTmp));
+ SwCellFrame const*const pOldF(static_cast<SwCellFrame*>(*iter));
+ bool bFollowFound(false);
+ for (SwCellFrame const* pOldIter = pOldF;
+ pOldIter; pOldIter = pOldIter->GetFollowCell())
+ {
+ if (pOldIter == pNewF)
+ {
+ bFollowFound = true;
+ break;
+ }
+ }
+ assert(bFollowFound);
+ }
+ else if (pTmp->IsFlowFrame())
+ {
+ SwFlowFrame const*const pNewF(SwFlowFrame::CastFlowFrame(pTmp));
+ SwFlowFrame const*const pOldF(SwFlowFrame::CastFlowFrame(*iter));
+ bool bFollowFound(false);
+ for (SwFlowFrame const* pOldIter = pOldF;
+ pOldIter; pOldIter = pOldIter->GetFollow())
+ {
+ if (pOldIter == pNewF)
+ {
+ bFollowFound = true;
+ break;
+ }
+ }
+ assert(bFollowFound);
+ }
+ pTmp = pTmp->GetUpper();
+ }
+ assert(pTmp == nullptr /* SwFlyAtContentFrame case */
+ || pTmp->IsPageFrame() // usual case
+ // the new page has columns, but the old page did not
+ || (pTmp->IsColumnFrame() && pTmp->GetUpper()->IsBodyFrame()
+ && pTmp->GetUpper()->GetUpper()->IsPageFrame()));
+ }
+#endif
+
+ // If our section frame has a successor then that has to be
+ // moved behind the new Follow of the section frames
+ SwFrame* pTmp = pSect->GetNext();
+ if( pTmp && pTmp != pSect->GetFollow() )
+ {
+ SwFlowFrame* pNxt;
+ SwContentFrame* pNxtContent = nullptr;
+ if( pTmp->IsContentFrame() )
+ {
+ pNxt = static_cast<SwContentFrame*>(pTmp);
+ pNxtContent = static_cast<SwContentFrame*>(pTmp);
+ }
+ else
+ {
+ pNxtContent = static_cast<SwLayoutFrame*>(pTmp)->ContainsContent();
+ if( pTmp->IsSctFrame() )
+ pNxt = static_cast<SwSectionFrame*>(pTmp);
+ else
+ {
+ assert(pTmp->IsTabFrame());
+ pNxt = static_cast<SwTabFrame*>(pTmp);
+ }
+ while( !pNxtContent && nullptr != ( pTmp = pTmp->GetNext() ) )
+ {
+ if( pTmp->IsContentFrame() )
+ pNxtContent = static_cast<SwContentFrame*>(pTmp);
+ else
+ pNxtContent = static_cast<SwLayoutFrame*>(pTmp)->ContainsContent();
+ }
+ }
+ if( pNxtContent )
+ {
+ SwFootnoteBossFrame* pOldBoss = pSect->FindFootnoteBossFrame( true );
+ if( pOldBoss == pNxtContent->FindFootnoteBossFrame( true ) )
+ {
+ SwSaveFootnoteHeight aHeight( pOldBoss,
+ pOldBoss->getFrameArea().Top() + pOldBoss->getFrameArea().Height() );
+ pSect->GetUpper()->MoveLowerFootnotes( pNxtContent, pOldBoss,
+ pLayLeaf->FindFootnoteBossFrame( true ), false );
+ }
+ }
+ pNxt->MoveSubTree( pLayLeaf, pNew->GetNext() );
+ }
+ if( pNew->GetFollow() )
+ pNew->SimpleFormat();
+ }
+ // The wanted layout sheet is now the first of the determined SctFrames:
+ pLayLeaf = pNew ? FirstLeaf(pNew) : nullptr;
+ }
+ return pLayLeaf;
+}
+
+/// Returns the preceding layout sheet where the frame can be moved into
+SwLayoutFrame *SwFrame::GetPrevSctLeaf()
+{
+ PROTOCOL_ENTER( this, PROT::Leaf, DbgAction::PrevSect, GetUpper()->FindSctFrame() )
+
+ SwLayoutFrame* pCol;
+ // ColumnFrame always contain a BodyFrame now
+ if( IsColBodyFrame() )
+ pCol = GetUpper();
+ else if( GetUpper()->IsColBodyFrame() )
+ pCol = GetUpper()->GetUpper();
+ else
+ pCol = nullptr;
+ bool bJump = false;
+ if( pCol )
+ {
+ if( pCol->GetPrev() )
+ {
+ do
+ {
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetPrev());
+ // Is there any content?
+ if( static_cast<SwLayoutFrame*>(pCol->Lower())->Lower() )
+ {
+ if( bJump ) // Did we skip a blank page?
+ SwFlowFrame::SetMoveBwdJump( true );
+ return static_cast<SwLayoutFrame*>(pCol->Lower()); // The columnm body
+ }
+ bJump = true;
+ } while( pCol->GetPrev() );
+
+ // We get here when all columns are empty, pCol is now the
+ // first column, we need the body though
+ pCol = static_cast<SwLayoutFrame*>(pCol->Lower());
+ }
+ else
+ pCol = nullptr;
+ }
+
+ if( bJump ) // Did we skip a blank page?
+ SwFlowFrame::SetMoveBwdJump( true );
+
+ SwSectionFrame *pSect = FindSctFrame();
+ if (!pCol && pSect && IsInTab() && CanContainSplitSection(this))
+ {
+ // We don't have a previous section yet, and we're in a
+ // section-in-table.
+ if (SwFlowFrame* pPrecede = pSect->GetPrecede())
+ {
+ // Our section has a precede, work with that.
+ if (pPrecede->GetFrame().IsLayoutFrame())
+ pCol = static_cast<SwLayoutFrame*>(&pPrecede->GetFrame());
+ }
+ }
+
+ // Within sections in tables or section in headers/footers there can
+ // be only one column change be made, one of the above shortcuts should
+ // have applied, also when the section has a pPrev.
+ // Now we even consider an empty column...
+ OSL_ENSURE( pSect, "GetNextSctLeaf: Missing SectionFrame" );
+ if (!pSect || (IsInTab() && !IsTabFrame()) || FindFooterOrHeader())
+ return pCol;
+
+ // === IMPORTANT ===
+ // Precondition, which needs to be hold, is that the <this> frame can be
+ // inside a table, but then the found section frame <pSect> is also inside
+ // this table.
+
+ // #i95698#
+ // A table cell containing directly a section does not break - see lcl_FindSectionsInRow(..)
+ // Thus, a table inside a section, which is inside another table can only
+ // flow backward in the columns of its section.
+ // Note: The table cell, which contains the section, can not have a master table cell.
+ if ( IsTabFrame() && pSect->IsInTab() )
+ {
+ return pCol;
+ }
+
+ {
+ if (SwFrame *pPrv = pSect->GetIndPrev())
+ {
+ // Mooching, half dead SectionFrames shouldn't confuse us
+ while( pPrv && pPrv->IsSctFrame() && !static_cast<SwSectionFrame*>(pPrv)->GetSection() )
+ pPrv = pPrv->GetPrev();
+ if( pPrv )
+ return pCol;
+ }
+ }
+
+ const bool bBody = IsInDocBody();
+ const bool bFly = IsInFly();
+
+ SwLayoutFrame *pLayLeaf = GetPrevLayoutLeaf();
+ SwLayoutFrame *pPrevLeaf = nullptr;
+
+ while ( pLayLeaf )
+ {
+ // Never step into tables or sections
+ if ( pLayLeaf->IsInTab() || pLayLeaf->IsInSct() )
+ {
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ }
+ else if ( bBody && pLayLeaf->IsInDocBody() )
+ {
+ // If there is a pLayLeaf has a lower pLayLeaf is the frame we are looking for.
+ // Exception: pLayLeaf->Lower() is a zombie section frame
+ const SwFrame* pTmp = pLayLeaf->Lower();
+ // OD 11.04.2003 #108824# - consider, that the zombie section frame
+ // can have frame below it in the found layout leaf.
+ // Thus, skipping zombie section frame, if possible.
+ while ( pTmp && pTmp->IsSctFrame() &&
+ !( static_cast<const SwSectionFrame*>(pTmp)->GetSection() ) &&
+ pTmp->GetNext()
+ )
+ {
+ pTmp = pTmp->GetNext();
+ }
+ if ( pTmp &&
+ ( !pTmp->IsSctFrame() ||
+ ( static_cast<const SwSectionFrame*>(pTmp)->GetSection() )
+ )
+ )
+ {
+ break;
+ }
+ pPrevLeaf = pLayLeaf;
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ if ( pLayLeaf )
+ SwFlowFrame::SetMoveBwdJump( true );
+ }
+ else if ( bFly )
+ break; // Contents in Flys every layout sheet should be right. Why?
+ else
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ }
+ if( !pLayLeaf )
+ {
+ if( !pPrevLeaf )
+ return pCol;
+ pLayLeaf = pPrevLeaf;
+ }
+
+ SwSectionFrame* pNew = nullptr;
+ // At first go to the end of the layout sheet
+ SwFrame *pTmp = pLayLeaf->Lower();
+ if( pTmp )
+ {
+ while( pTmp->GetNext() )
+ pTmp = pTmp->GetNext();
+ if( pTmp->IsSctFrame() )
+ {
+ // Half dead ones only interfere here
+ while( !static_cast<SwSectionFrame*>(pTmp)->GetSection() && pTmp->GetPrev() &&
+ pTmp->GetPrev()->IsSctFrame() )
+ pTmp = pTmp->GetPrev();
+ if( static_cast<SwSectionFrame*>(pTmp)->GetFollow() == pSect )
+ pNew = static_cast<SwSectionFrame*>(pTmp);
+ }
+ }
+ if( !pNew )
+ {
+ pNew = new SwSectionFrame( *pSect, true );
+ pNew->InsertBefore( pLayLeaf, nullptr );
+ pNew->Init();
+ SwRectFnSet aRectFnSet(pNew);
+ aRectFnSet.MakePos( *pNew, pLayLeaf, pNew->GetPrev(), true );
+
+ pLayLeaf = FirstLeaf( pNew );
+ if( !pNew->Lower() ) // Format single column sections
+ {
+ pNew->MakePos();
+ pLayLeaf->Format(getRootFrame()->GetCurrShell()->GetOut()); // In order that the PrtArea is correct for the MoveBwd
+ }
+ else
+ pNew->SimpleFormat();
+ }
+ else
+ {
+ pLayLeaf = FirstLeaf( pNew );
+ if( pLayLeaf->IsColBodyFrame() )
+ {
+ // In existent section columns we're looking for the last not empty
+ // column.
+ SwLayoutFrame *pTmpLay = pLayLeaf;
+ while( pLayLeaf->GetUpper()->GetNext() )
+ {
+ pLayLeaf = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLayLeaf->GetUpper()->GetNext())->Lower());
+ if( pLayLeaf->Lower() )
+ pTmpLay = pLayLeaf;
+ }
+ // If we skipped an empty column, we've to set the jump-flag
+ if( pLayLeaf != pTmpLay )
+ {
+ pLayLeaf = pTmpLay;
+ SwFlowFrame::SetMoveBwdJump( true );
+ }
+ }
+ }
+ return pLayLeaf;
+}
+
+static SwTwips lcl_DeadLine( const SwFrame* pFrame )
+{
+ const SwLayoutFrame* pUp = pFrame->GetUpper();
+ while( pUp && pUp->IsInSct() )
+ {
+ if( pUp->IsSctFrame() )
+ pUp = pUp->GetUpper();
+ // Columns now with BodyFrame
+ else if( pUp->IsColBodyFrame() && pUp->GetUpper()->GetUpper()->IsSctFrame() )
+ pUp = pUp->GetUpper()->GetUpper();
+ else
+ break;
+ }
+ SwRectFnSet aRectFnSet(pFrame);
+ return pUp ? aRectFnSet.GetPrtBottom(*pUp) :
+ aRectFnSet.GetBottom(pFrame->getFrameArea());
+}
+
+/// checks whether the SectionFrame is still able to grow, as case may be the environment has to be asked
+bool SwSectionFrame::Growable() const
+{
+ SwRectFnSet aRectFnSet(this);
+ if( aRectFnSet.YDiff( lcl_DeadLine( this ),
+ aRectFnSet.GetBottom(getFrameArea()) ) > 0 )
+ return true;
+
+ return ( GetUpper() && const_cast<SwFrame*>(static_cast<SwFrame const *>(GetUpper()))->Grow( LONG_MAX, true ) );
+}
+
+SwTwips SwSectionFrame::Grow_( SwTwips nDist, bool bTst )
+{
+ if ( !IsColLocked() && !HasFixSize() )
+ {
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nFrameHeight > 0 && nDist > (LONG_MAX - nFrameHeight) )
+ nDist = LONG_MAX - nFrameHeight;
+
+ if ( nDist <= 0 )
+ return 0;
+
+ bool bInCalcContent = GetUpper() && IsInFly() && FindFlyFrame()->IsLocked();
+ // OD 2004-03-15 #116561# - allow grow in online layout
+ bool bGrow = !Lower() || !Lower()->IsColumnFrame() || !Lower()->GetNext();
+ if (!bGrow)
+ {
+ SwSection* pSection = GetSection();
+ bGrow = pSection && pSection->GetFormat()->GetBalancedColumns().GetValue();
+ }
+ if( !bGrow )
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ bGrow = pSh && pSh->GetViewOptions()->getBrowseMode();
+ }
+ if( bGrow )
+ {
+ SwTwips nGrow;
+ if( IsInFootnote() )
+ nGrow = 0;
+ else
+ {
+ nGrow = lcl_DeadLine( this );
+ nGrow = aRectFnSet.YDiff( nGrow, aRectFnSet.GetBottom(getFrameArea()) );
+ }
+ SwTwips nSpace = nGrow;
+ if( !bInCalcContent && nGrow < nDist && GetUpper() )
+ nGrow = o3tl::saturating_add(
+ nGrow, GetUpper()->Grow( LONG_MAX, true ));
+
+ if( nGrow > nDist )
+ nGrow = nDist;
+ if( nGrow <= 0 )
+ {
+ nGrow = 0;
+ if (!bTst)
+ {
+ if( bInCalcContent )
+ InvalidateSize_();
+ else
+ InvalidateSize();
+ }
+ }
+ else if( !bTst )
+ {
+ if( bInCalcContent )
+ InvalidateSize_();
+ else if( nSpace < nGrow && nDist != nSpace + GetUpper()->
+ Grow( nGrow - nSpace ) )
+ InvalidateSize();
+ else
+ {
+ const SvxGraphicPosition ePos =
+ GetAttrSet()->GetBackground().GetGraphicPos();
+ if ( GPOS_RT < ePos && GPOS_TILED != ePos )
+ {
+ SetCompletePaint();
+ InvalidatePage();
+ }
+ if( GetUpper() && GetUpper()->IsHeaderFrame() )
+ GetUpper()->InvalidateSize();
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nGrow );
+ }
+
+ {
+ const tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()) + nGrow;
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nPrtHeight );
+ }
+
+ if( Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() )
+ {
+ SwFrame* pTmp = Lower();
+ do
+ {
+ pTmp->InvalidateSize_();
+ pTmp = pTmp->GetNext();
+ } while ( pTmp );
+ InvalidateSize_();
+ }
+ if( GetNext() )
+ {
+ // Own height changed, need to invalidate the position of
+ // next frames.
+ SwFrame *pFrame = GetNext();
+ while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
+ {
+ // Invalidate all in-between frames, otherwise position
+ // calculation (which only looks back to one relative
+ // frame) will have an incorrect result.
+ InvalidateFramePos(pFrame, bInCalcContent);
+ pFrame = pFrame->GetNext();
+ }
+ if( pFrame )
+ {
+ InvalidateFramePos(pFrame, bInCalcContent);
+ }
+ }
+ // #i28701# - Due to the new object positioning
+ // the frame on the next page/column can flow backward (e.g. it
+ // was moved forward due to the positioning of its objects ).
+ // Thus, invalivate this next frame, if document compatibility
+ // option 'Consider wrapping style influence on object positioning' is ON.
+ else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ InvalidateNextPos();
+ }
+ }
+ return nGrow;
+ }
+ if ( !bTst )
+ {
+ if( bInCalcContent )
+ InvalidateSize_();
+ else
+ InvalidateSize();
+ }
+ }
+ return 0;
+}
+
+SwTwips SwSectionFrame::Shrink_( SwTwips nDist, bool bTst )
+{
+ if ( Lower() && !IsColLocked() && !HasFixSize() )
+ {
+ if( ToMaximize( false ) )
+ {
+ if( !bTst )
+ InvalidateSize();
+ }
+ else
+ {
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if ( nDist > nFrameHeight )
+ nDist = nFrameHeight;
+
+ if ( Lower()->IsColumnFrame() && Lower()->GetNext() && // FootnoteAtEnd
+ !GetSection()->GetFormat()->GetBalancedColumns().GetValue() )
+ { // With column bases the format takes over the control of the
+ // growth (because of the balance)
+ if ( !bTst )
+ InvalidateSize();
+ return nDist;
+ }
+ else if( !bTst )
+ {
+ const SvxGraphicPosition ePos =
+ GetAttrSet()->GetBackground().GetGraphicPos();
+ if ( GPOS_RT < ePos && GPOS_TILED != ePos )
+ {
+ SetCompletePaint();
+ InvalidatePage();
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, -nDist );
+ }
+
+ {
+ const tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()) - nDist;
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nPrtHeight );
+ }
+
+ // We do not allow a section frame to shrink the its upper
+ // footer frame. This is because in the calculation of a
+ // footer frame, the content of the section frame is _not_
+ // calculated. If there is a fly frame overlapping with the
+ // footer frame, the section frame is not affected by this
+ // during the calculation of the footer frame size.
+ // The footer frame does not grow in its FormatSize function
+ // but during the calculation of the content of the section
+ // frame. The section frame grows until some of its text is
+ // located on top of the fly frame. The next call of CalcContent
+ // tries to shrink the section and here it would also shrink
+ // the footer. This may not happen, because shrinking the footer
+ // would cause the top of the section frame to overlap with the
+ // fly frame again, this would result in a perfect loop.
+ if( GetUpper() && !GetUpper()->IsFooterFrame() )
+ GetUpper()->Shrink( nDist, bTst );
+
+ if( Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() )
+ {
+ SwFrame* pTmp = Lower();
+ do
+ {
+ pTmp->InvalidateSize_();
+ pTmp = pTmp->GetNext();
+ } while ( pTmp );
+ }
+ if( GetNext() )
+ {
+ SwFrame* pFrame = GetNext();
+ while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
+ pFrame = pFrame->GetNext();
+ if( pFrame )
+ pFrame->InvalidatePos();
+ else
+ SetRetouche();
+ }
+ else
+ SetRetouche();
+ return nDist;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+|* When are Frames within a SectionFrames moveable?
+|* If they are not in the last column of a SectionFrames yet,
+|* if there is no Follow,
+|* if the SectionFrame cannot grow anymore, then it gets more complicated,
+|* in that case it depends on whether the SectionFrame can find a next
+|* layout sheet. In (column based/chained) Flys this is checked via
+|* GetNextLayout, in tables and headers/footers there is none, however in the
+|* DocBody and in foot notes there is always one.
+|*
+|* This routine is used in the TextFormatter to decided whether it's allowed to
+|* create a (paragraph-)Follow or whether the paragraph has to stick together
+|*/
+bool SwSectionFrame::MoveAllowed( const SwFrame* pFrame) const
+{
+ // Is there a Follow or is the Frame not in the last column?
+ if( HasFollow() || ( pFrame->GetUpper()->IsColBodyFrame() &&
+ pFrame->GetUpper()->GetUpper()->GetNext() ) )
+ return true;
+ if( pFrame->IsInFootnote() )
+ {
+ if( IsInFootnote() )
+ {
+ if( GetUpper()->IsInSct() )
+ {
+ if( Growable() )
+ return false;
+ return GetUpper()->FindSctFrame()->MoveAllowed( this );
+ }
+ else
+ return true;
+ }
+ // The content of footnote inside a columned sectionfrm is moveable
+ // except in the last column
+ const SwLayoutFrame *pLay = pFrame->FindFootnoteFrame()->GetUpper()->GetUpper();
+ if( pLay->IsColumnFrame() && pLay->GetNext() )
+ {
+ // The first paragraph in the first footnote in the first column
+ // in the sectionfrm at the top of the page is not moveable,
+ // if the columnbody is empty.
+ bool bRet = false;
+ if( pLay->GetIndPrev() || pFrame->GetIndPrev() ||
+ pFrame->FindFootnoteFrame()->GetPrev() )
+ bRet = true;
+ else
+ {
+ const SwLayoutFrame* pBody = static_cast<const SwColumnFrame*>(pLay)->FindBodyCont();
+ if( pBody && pBody->Lower() )
+ bRet = true;
+ }
+ if( bRet && ( IsFootnoteAtEnd() || !Growable() ) )
+ return true;
+ }
+ }
+ // Or can the section still grow?
+ if( !IsColLocked() && Growable() )
+ return false;
+ // Now it has to be examined whether there is a layout sheet wherein
+ // a section Follow can be created
+ if( !CanContainSplitSection(this) || ( !IsInDocBody() && FindFooterOrHeader() ) )
+ return false; // It doesn't work in table-in-sections/nested tables/headers/footers
+ if( IsInFly() ) // In column based or chained frames
+ return nullptr != const_cast<SwFrame*>(static_cast<SwFrame const *>(GetUpper()))->GetNextLeaf( MAKEPAGE_NONE );
+ return true;
+}
+
+/** Called for a frame inside a section with no direct previous frame (or only
+ previous empty section frames) the previous frame of the outer section is
+ returned, if the frame is the first flowing content of this section.
+
+ Note: For a frame inside a table frame, which is inside a section frame,
+ NULL is returned.
+*/
+SwFrame* SwFrame::GetIndPrev_() const
+{
+ SwFrame *pRet = nullptr;
+ // #i79774#
+ // Do not assert, if the frame has a direct previous frame, because it
+ // could be an empty section frame. The caller has to assure, that the
+ // frame has no direct previous frame or only empty section frames as
+ // previous frames.
+ OSL_ENSURE( /*!pPrev &&*/ IsInSct(), "Why?" );
+ const SwFrame* pSct = GetUpper();
+ if( !pSct )
+ return nullptr;
+ if( pSct->IsSctFrame() )
+ pRet = pSct->GetIndPrev();
+ else if( pSct->IsColBodyFrame() && (pSct = pSct->GetUpper()->GetUpper())->IsSctFrame() )
+ {
+ // Do not return the previous frame of the outer section, if in one
+ // of the previous columns is content.
+ const SwFrame* pCol = GetUpper()->GetUpper()->GetPrev();
+ while( pCol )
+ {
+ assert(pCol->IsColumnFrame());
+ assert(pCol->GetLower() && pCol->GetLower()->IsBodyFrame());
+ if( static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol)->Lower())->Lower() )
+ return nullptr;
+ pCol = pCol->GetPrev();
+ }
+ pRet = pSct->GetIndPrev();
+ }
+
+ // skip empty section frames
+ while( pRet && pRet->IsSctFrame() && !static_cast<SwSectionFrame*>(pRet)->GetSection() )
+ pRet = pRet->GetIndPrev();
+ return pRet;
+}
+
+SwFrame* SwFrame::GetIndNext_()
+{
+ OSL_ENSURE( !mpNext && IsInSct(), "Why?" );
+ SwFrame* pSct = GetUpper();
+ if( !pSct )
+ return nullptr;
+ if( pSct->IsSctFrame() )
+ return pSct->GetIndNext();
+ if( pSct->IsColBodyFrame() && (pSct = pSct->GetUpper()->GetUpper())->IsSctFrame() )
+ { // We can only return the successor of the SectionFrames if there is no
+ // content in the successive columns
+ SwFrame* pCol = GetUpper()->GetUpper()->GetNext();
+ while( pCol )
+ {
+ assert(pCol->IsColumnFrame());
+ assert(pCol->GetLower() && pCol->GetLower()->IsBodyFrame());
+ if( static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pCol)->Lower())->Lower() )
+ return nullptr;
+ pCol = pCol->GetNext();
+ }
+ return pSct->GetIndNext();
+ }
+ return nullptr;
+}
+
+bool SwSectionFrame::IsDescendantFrom( const SwSectionFormat* pFormat ) const
+{
+ if( !m_pSection || !pFormat )
+ return false;
+ const SwSectionFormat *pMyFormat = m_pSection->GetFormat();
+ while( pFormat != pMyFormat )
+ {
+ if( auto pNewFormat = dynamic_cast< const SwSectionFormat *>( pMyFormat->GetRegisteredIn()) )
+ pMyFormat = pNewFormat;
+ else
+ return false;
+ }
+ return true;
+}
+
+void SwSectionFrame::CalcFootnoteAtEndFlag()
+{
+ SwSectionFormat *pFormat = GetSection()->GetFormat();
+ sal_uInt16 nVal = pFormat->GetFootnoteAtTextEnd( false ).GetValue();
+ m_bFootnoteAtEnd = FTNEND_ATPGORDOCEND != nVal;
+ m_bOwnFootnoteNum = FTNEND_ATTXTEND_OWNNUMSEQ == nVal ||
+ FTNEND_ATTXTEND_OWNNUMANDFMT == nVal;
+ while( !m_bFootnoteAtEnd && !m_bOwnFootnoteNum )
+ {
+ if( auto pNewFormat = dynamic_cast<SwSectionFormat *>( pFormat->GetRegisteredIn()) )
+ pFormat = pNewFormat;
+ else
+ break;
+ nVal = pFormat->GetFootnoteAtTextEnd( false ).GetValue();
+ if( FTNEND_ATPGORDOCEND != nVal )
+ {
+ m_bFootnoteAtEnd = true;
+ m_bOwnFootnoteNum = m_bOwnFootnoteNum ||FTNEND_ATTXTEND_OWNNUMSEQ == nVal ||
+ FTNEND_ATTXTEND_OWNNUMANDFMT == nVal;
+ }
+ }
+}
+
+bool SwSectionFrame::IsEndnoteAtMyEnd() const
+{
+ return m_pSection->GetFormat()->GetEndAtTextEnd( false ).IsAtEnd();
+}
+
+void SwSectionFrame::CalcEndAtEndFlag()
+{
+ SwSectionFormat *pFormat = GetSection()->GetFormat();
+ m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd();
+ while( !m_bEndnAtEnd )
+ {
+ if( auto pNewFormat = dynamic_cast<SwSectionFormat *>( pFormat->GetRegisteredIn()) )
+ pFormat = pNewFormat;
+ else
+ break;
+ m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd();
+ }
+}
+
+void SwSectionFrame::Notify(SfxHint const& rHint)
+{
+ SwSectionFormat *const pFormat(GetSection()->GetFormat());
+ assert(pFormat);
+ SwClientNotify(*pFormat, rHint);
+}
+
+void SwSectionFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SwSectionFrameInvFlags eInvFlags = SwSectionFrameInvFlags::NONE;
+ if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
+ {
+ auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ SfxItemIter aOIter(*rOldSetChg.GetChgSet());
+ SfxItemIter aNIter(*rNewSetChg.GetChgSet());
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ SwAttrSetChg aOldSet(rOldSetChg);
+ SwAttrSetChg aNewSet(rNewSetChg);
+ do
+ {
+ UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
+ pNItem = aNIter.NextItem();
+ pOItem = aOIter.NextItem();
+ } while (pNItem);
+ if(aOldSet.Count() || aNewSet.Count())
+ SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ else
+ UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if (eInvFlags != SwSectionFrameInvFlags::NONE)
+ {
+ if(eInvFlags & SwSectionFrameInvFlags::InvalidateSize)
+ InvalidateSize();
+ if(eInvFlags & SwSectionFrameInvFlags::SetCompletePaint)
+ SetCompletePaint();
+ }
+ }
+ else if(const auto pHint = dynamic_cast<const SwSectionFrameMoveAndDeleteHint*>(&rHint))
+ {
+ // #i117863#
+ if(&rMod != GetDep())
+ return;
+ SwSectionFrame::MoveContentAndDelete(this, pHint->IsSaveContent());
+ }
+ else
+ SwFrame::SwClientNotify(rMod, rHint);
+}
+
+void SwSectionFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
+ SwSectionFrameInvFlags &rInvFlags,
+ SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
+{
+ bool bClear = true;
+ const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ switch( nWhich )
+ { // Suppress multi columns in foot notes
+ case RES_FMT_CHG:
+ {
+ const SwFormatCol& rNewCol = GetFormat()->GetCol();
+ if( !IsInFootnote() )
+ {
+ // Nasty case. When allocating a template we can not count
+ // on the old column attribute. We're left with creating a
+ // temporary attribute here.
+ SwFormatCol aCol;
+ if ( Lower() && Lower()->IsColumnFrame() )
+ {
+ sal_uInt16 nCol = 0;
+ SwFrame *pTmp = Lower();
+ do
+ { ++nCol;
+ pTmp = pTmp->GetNext();
+ } while ( pTmp );
+ aCol.Init( nCol, 0, 1000 );
+ }
+ bool bChgFootnote = IsFootnoteAtEnd();
+ bool const bChgEndn = IsEndnAtEnd();
+ bool const bChgMyEndn = IsEndnoteAtMyEnd();
+ CalcFootnoteAtEndFlag();
+ CalcEndAtEndFlag();
+ bChgFootnote = ( bChgFootnote != IsFootnoteAtEnd() ) ||
+ ( bChgEndn != IsEndnAtEnd() ) ||
+ ( bChgMyEndn != IsEndnoteAtMyEnd() );
+ ChgColumns( aCol, rNewCol, bChgFootnote );
+ rInvFlags |= SwSectionFrameInvFlags::SetCompletePaint;
+ }
+ rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
+ bClear = false;
+ }
+ break;
+
+ case RES_COL:
+ if( !IsInFootnote() )
+ {
+ assert(pOld && pNew);
+ if (pOld && pNew)
+ {
+ ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
+ rInvFlags |= SwSectionFrameInvFlags::InvalidateSize | SwSectionFrameInvFlags::SetCompletePaint;
+ }
+ }
+ break;
+
+ case RES_FTN_AT_TXTEND:
+ if( !IsInFootnote() )
+ {
+ bool const bOld = IsFootnoteAtEnd();
+ CalcFootnoteAtEndFlag();
+ if (bOld != IsFootnoteAtEnd())
+ {
+ const SwFormatCol& rNewCol = GetFormat()->GetCol();
+ ChgColumns( rNewCol, rNewCol, true );
+ rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
+ }
+ }
+ break;
+
+ case RES_END_AT_TXTEND:
+ if( !IsInFootnote() )
+ {
+ bool const bOld = IsEndnAtEnd();
+ bool const bMyOld = IsEndnoteAtMyEnd();
+ CalcEndAtEndFlag();
+ if (bOld != IsEndnAtEnd() || bMyOld != IsEndnoteAtMyEnd())
+ {
+ const SwFormatCol& rNewCol = GetFormat()->GetCol();
+ ChgColumns( rNewCol, rNewCol, true );
+ rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
+ }
+ }
+ break;
+ case RES_COLUMNBALANCE:
+ rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
+ break;
+
+ case RES_FRAMEDIR :
+ SetDerivedR2L( false );
+ CheckDirChange();
+ break;
+
+ case RES_PROTECT:
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetLayout()->IsAnyShellAccessible() )
+ pSh->Imp()->InvalidateAccessibleEditableState( true, this );
+ }
+#endif
+ break;
+
+ default:
+ bClear = false;
+ }
+ if ( !bClear )
+ return;
+
+ if ( pOldSet || pNewSet )
+ {
+ if ( pOldSet )
+ pOldSet->ClearItem( nWhich );
+ if ( pNewSet )
+ pNewSet->ClearItem( nWhich );
+ }
+ else
+ {
+ SwModify aMod;
+ SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ }
+}
+
+/// A follow or a ftncontainer at the end of the page causes a maximal Size of the sectionframe.
+bool SwSectionFrame::ToMaximize( bool bCheckFollow ) const
+{
+ if( HasFollow() )
+ {
+ if( !bCheckFollow ) // Don't check superfluous follows
+ return true;
+ const SwSectionFrame* pFoll = GetFollow();
+ while( pFoll && pFoll->IsSuperfluous() )
+ pFoll = pFoll->GetFollow();
+ if( pFoll )
+ return true;
+ }
+ if( IsFootnoteAtEnd() )
+ return false;
+ const SwFootnoteContFrame* pCont = ContainsFootnoteCont();
+ if( !IsEndnAtEnd() )
+ return nullptr != pCont;
+ bool bRet = false;
+ while( pCont && !bRet )
+ {
+ if( pCont->FindFootNote() )
+ bRet = true;
+ else
+ pCont = ContainsFootnoteCont( pCont );
+ }
+ return bRet;
+}
+
+/// Check every Column for FootnoteContFrames.
+SwFootnoteContFrame* SwSectionFrame::ContainsFootnoteCont( const SwFootnoteContFrame* pCont ) const
+{
+ SwFootnoteContFrame* pRet = nullptr;
+ const SwLayoutFrame* pLay;
+ if( pCont )
+ {
+ pLay = pCont->FindFootnoteBossFrame();
+ OSL_ENSURE( IsAnLower( pLay ), "ContainsFootnoteCont: Wrong FootnoteContainer" );
+ pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
+ }
+ else if( Lower() && Lower()->IsColumnFrame() )
+ pLay = static_cast<const SwLayoutFrame*>(Lower());
+ else
+ pLay = nullptr;
+ while ( !pRet && pLay )
+ {
+ if( pLay->Lower() && pLay->Lower()->GetNext() )
+ {
+ OSL_ENSURE( pLay->Lower()->GetNext()->IsFootnoteContFrame(),
+ "ToMaximize: Unexpected Frame" );
+ pRet = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pLay->Lower()->GetNext()));
+ }
+ OSL_ENSURE( !pLay->GetNext() || pLay->GetNext()->IsLayoutFrame(),
+ "ToMaximize: ColFrame expected" );
+ pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
+ }
+ return pRet;
+}
+
+void SwSectionFrame::InvalidateFootnotePos()
+{
+ SwFootnoteContFrame* pCont = ContainsFootnoteCont();
+ if( pCont )
+ {
+ SwFrame *pTmp = pCont->ContainsContent();
+ if( pTmp )
+ pTmp->InvalidatePos_();
+ }
+}
+
+SwTwips SwSectionFrame::CalcUndersize() const
+{
+ SwRectFnSet aRectFnSet(this);
+ return InnerHeight() - aRectFnSet.GetHeight(getFramePrintArea());
+}
+
+SwTwips SwSectionFrame::Undersize()
+{
+ const auto nRet = CalcUndersize();
+ m_bUndersized = (nRet > 0);
+ return nRet <= 0 ? 0 : nRet;
+}
+
+void SwSectionFrame::CalcFootnoteContent()
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ SwFootnoteContFrame* pCont = ContainsFootnoteCont();
+ if( !pCont )
+ return;
+
+ SwFrame* pFrame = pCont->ContainsAny();
+ if( pFrame )
+ pCont->Calc(pRenderContext);
+ while( pFrame && IsAnLower( pFrame ) )
+ {
+ SwFootnoteFrame* pFootnote = pFrame->FindFootnoteFrame();
+ if( pFootnote )
+ pFootnote->Calc(pRenderContext);
+ pFrame->Calc(pRenderContext);
+ if( pFrame->IsSctFrame() )
+ {
+ SwFrame *pTmp = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pTmp )
+ {
+ pFrame = pTmp;
+ continue;
+ }
+ }
+ pFrame = pFrame->FindNext();
+ }
+}
+
+/*
+ * If a SectionFrame gets empty, e.g. because its content changes the page/column,
+ * it is not destroyed immediately (there could be a pointer left to it on the
+ * stack), instead it puts itself in a list at the RootFrame, which is processed
+ * later on (in Layaction::Action among others). Its size is set to Null and
+ * the pointer to its page as well. Such SectionFrames that are to be deleted
+ * must be ignored by the layout/during formatting.
+ *
+ * With InsertEmptySct the RootFrame stores a SectionFrame in the list,
+ * with RemoveFromList it can be removed from the list (Dtor),
+ * with DeleteEmptySct the list is processed and the SectionFrames are destroyed.
+ */
+void SwRootFrame::InsertEmptySct( SwSectionFrame* pDel )
+{
+ if( !mpDestroy )
+ mpDestroy.reset( new SwDestroyList );
+ mpDestroy->insert( pDel );
+}
+
+void SwRootFrame::DeleteEmptySct_()
+{
+ assert(mpDestroy);
+ while( !mpDestroy->empty() )
+ {
+ SwSectionFrame* pSect = *mpDestroy->begin();
+ mpDestroy->erase( mpDestroy->begin() );
+ OSL_ENSURE( !pSect->IsColLocked() && !pSect->IsJoinLocked(),
+ "DeleteEmptySct: Locked SectionFrame" );
+ SAL_WARN_IF(pSect->IsDeleteForbidden(), "sw.layout", "not allowed delete SwFrame");
+ if( !pSect->getFrameArea().HasArea() && !pSect->ContainsContent() && !pSect->IsDeleteForbidden() )
+ {
+ SwLayoutFrame* pUp = pSect->GetUpper();
+ pSect->RemoveFromLayout();
+ SwFrame::DestroyFrame(pSect);
+ if( pUp && !pUp->Lower() )
+ {
+ if( pUp->IsPageBodyFrame() )
+ pUp->getRootFrame()->SetSuperfluous();
+ else if( pUp->IsFootnoteFrame() && !pUp->IsColLocked() &&
+ pUp->GetUpper() )
+ {
+ pUp->Cut();
+ SwFrame::DestroyFrame(pUp);
+ }
+ }
+ }
+ else {
+ OSL_ENSURE( pSect->GetSection(), "DeleteEmptySct: Half-dead SectionFrame?!" );
+ }
+ }
+}
+
+void SwRootFrame::RemoveFromList_( SwSectionFrame* pSct )
+{
+ assert(mpDestroy && "Where's my list?");
+ mpDestroy->erase( pSct );
+}
+
+#ifdef DBG_UTIL
+bool SwRootFrame::IsInDelList( SwSectionFrame* pSct ) const
+{
+ return mpDestroy && mpDestroy->find( pSct ) != mpDestroy->end();
+}
+#endif
+
+bool SwSectionFrame::IsBalancedSection() const
+{
+ bool bRet = false;
+ if ( GetSection() && Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() )
+ {
+ bRet = !GetSection()->GetFormat()->GetBalancedColumns().GetValue();
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/softpagebreak.cxx b/sw/source/core/layout/softpagebreak.cxx
new file mode 100644
index 000000000..87ba7c24e
--- /dev/null
+++ b/sw/source/core/layout/softpagebreak.cxx
@@ -0,0 +1,154 @@
+/* -*- 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 <txtfrm.hxx>
+#include <pagefrm.hxx>
+#include <swtable.hxx>
+#include <frmfmt.hxx>
+#include <rowfrm.hxx>
+#include <tabfrm.hxx>
+#include <calbck.hxx>
+#include <ndtxt.hxx>
+
+void SwTextNode::fillSoftPageBreakList( SwSoftPageBreakList& rBreak ) const
+{
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
+ for( const SwTextFrame *pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ // No soft page break in header or footer
+ if( pFrame->FindFooterOrHeader() || pFrame->IsInFly() )
+ return;
+ // No soft page break if I'm not the first frame in my layout frame
+ if( pFrame->GetIndPrev() )
+ continue;
+ const SwPageFrame* pPage = pFrame->FindPageFrame();
+ // No soft page break at the first page
+ if( pPage && pPage->GetPrev() )
+ {
+ const SwContentFrame* pFirst2 = pPage->FindFirstBodyContent();
+ // Special handling for content frame in table frames
+ if( pFrame->IsInTab() )
+ {
+ // No soft page break if I'm in a table but the first content frame
+ // at my page is not in a table
+ if( !pFirst2 || !pFirst2->IsInTab() )
+ continue;
+ const SwLayoutFrame *pRow = pFrame->GetUpper();
+ // Looking for the "most upper" row frame,
+ // skipping sub tables and/or table in table
+ while( !pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame() ||
+ pRow->GetUpper()->GetUpper()->IsInTab() )
+ pRow = pRow->GetUpper();
+ const SwTabFrame *pTab = pRow->FindTabFrame();
+ // For master tables the soft page break will exported at the table row,
+ // not at the content frame.
+ // If the first content is outside my table frame, no soft page break.
+ if( !pTab->IsFollow() || !pTab->IsAnLower( pFirst2 ) )
+ continue;
+ // Only content of non-heading-rows can get a soft page break
+ const SwFrame* pFirstRow = pTab->GetFirstNonHeadlineRow();
+ // If there's no follow flow line, the soft page break will be
+ // exported at the row, not at the content.
+ if( pRow == pFirstRow &&
+ pTab->FindMaster()->HasFollowFlowLine() )
+ {
+ // Now we have the row which causes a new page,
+ // this row is a follow flow line and therefore cannot get
+ // the soft page break itself.
+ // Every first content frame of every cell frame in this row
+ // will get the soft page break
+ const SwFrame* pCell = pRow->Lower();
+ while( pCell )
+ {
+ pFirst2 = static_cast<const SwLayoutFrame*>(pCell)->ContainsContent();
+ if( pFirst2 == pFrame )
+ { // Here we are: a first content inside a cell
+ // inside the split row => soft page break
+ auto const pos(pFrame->MapViewToModel(pFrame->GetOffset()));
+ if (pos.first == this)
+ {
+ rBreak.insert(pos.second);
+ }
+ break;
+ }
+ pCell = pCell->GetNext();
+ }
+ }
+ }
+ else // No soft page break if there's a "hard" page break attribute
+ if( pFirst2 == pFrame && !pFrame->IsPageBreak( true ) )
+ {
+ auto const pos(pFrame->MapViewToModel(pFrame->GetOffset()));
+ if (pos.first == this)
+ { // in the !Show case, we have to iterate over the merged
+ // SwTextFrame for every node
+ rBreak.insert(pos.second);
+ }
+ }
+ }
+ }
+}
+
+bool SwTableLine::hasSoftPageBreak() const
+{
+ // No soft page break for sub tables
+ if( GetUpper() || !GetFrameFormat() )
+ return false;
+ SwIterator<SwRowFrame,SwFormat> aIter( *GetFrameFormat() );
+ for( SwRowFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->GetTabLine() == this )
+ {
+ const SwTabFrame* pTab = pLast->FindTabFrame();
+ // No soft page break for
+ // tables with prevs, i.e. if the frame is not the first in its layout frame
+ // tables in footer or header
+ // tables in flies
+ // inner tables of nested tables
+ // master table frames with "hard" page break attribute
+ if( pTab->GetIndPrev() || pTab->FindFooterOrHeader()
+ || pTab->IsInFly() || pTab->GetUpper()->IsInTab() ||
+ ( !pTab->IsFollow() && pTab->IsPageBreak( true ) ) )
+ return false;
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+ // No soft page break at the first page of the document
+ if( pPage && !pPage->GetPrev() )
+ return false;
+ const SwContentFrame* pFirst = pPage ? pPage->FindFirstBodyContent() : nullptr;
+ // No soft page break for
+ // tables which does not contain the first body content of the page
+ if( !pFirst || !pTab->IsAnLower( pFirst->FindTabFrame() ) )
+ return false;
+ // The row which could get a soft page break must be either the first
+ // row of a master table frame or the first "non-headline-row" of a
+ // follow table frame...
+ const SwFrame* pRow = pTab->IsFollow() ?
+ pTab->GetFirstNonHeadlineRow() : pTab->Lower();
+ if( pRow == pLast )
+ {
+ // The last check: no soft page break for "follow" table lines
+ return !pTab->IsFollow() || !pTab->FindMaster()->HasFollowFlowLine();
+ }
+ return false;
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/sortedobjs.cxx b/sw/source/core/layout/sortedobjs.cxx
new file mode 100644
index 000000000..ad3759466
--- /dev/null
+++ b/sw/source/core/layout/sortedobjs.cxx
@@ -0,0 +1,293 @@
+/* -*- 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 <sortedobjs.hxx>
+
+#include <algorithm>
+#include <anchoredobject.hxx>
+#include <fmtanchr.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include <frmfmt.hxx>
+#include <pam.hxx>
+#include <svx/svdobj.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+SwSortedObjs::SwSortedObjs()
+{
+}
+
+SwSortedObjs::~SwSortedObjs()
+{
+}
+
+size_t SwSortedObjs::size() const
+{
+ return maSortedObjLst.size();
+}
+
+SwAnchoredObject* SwSortedObjs::operator[]( size_t _nIndex ) const
+{
+ SwAnchoredObject* pAnchoredObj = nullptr;
+
+ if ( _nIndex >= size() )
+ {
+ OSL_FAIL( "<SwSortedObjs::operator[]> - index out of range" );
+ }
+ else
+ {
+ pAnchoredObj = maSortedObjLst[ _nIndex ];
+ }
+
+ return pAnchoredObj;
+}
+
+namespace
+{
+ int GetAnchorWeight(RndStdIds eAnchor)
+ {
+ if (eAnchor == RndStdIds::FLY_AT_CHAR)
+ return 0;
+ if (eAnchor == RndStdIds::FLY_AS_CHAR)
+ return 1;
+ return 2;
+ }
+
+struct ObjAnchorOrder
+{
+ bool operator()( const SwAnchoredObject* _pListedAnchoredObj,
+ const SwAnchoredObject* _pNewAnchoredObj )
+ {
+ // get attributes of listed object
+ const SwFrameFormat& rFormatListed = _pListedAnchoredObj->GetFrameFormat();
+ const SwFormatAnchor* pAnchorListed = &(rFormatListed.GetAnchor());
+
+ // get attributes of new object
+ const SwFrameFormat& rFormatNew = _pNewAnchoredObj->GetFrameFormat();
+ const SwFormatAnchor* pAnchorNew = &(rFormatNew.GetAnchor());
+
+ // check for to-page anchored objects
+ if ((pAnchorListed->GetAnchorId() == RndStdIds::FLY_AT_PAGE) &&
+ (pAnchorNew ->GetAnchorId() != RndStdIds::FLY_AT_PAGE))
+ {
+ return true;
+ }
+ else if ((pAnchorListed->GetAnchorId() != RndStdIds::FLY_AT_PAGE) &&
+ (pAnchorNew ->GetAnchorId() == RndStdIds::FLY_AT_PAGE))
+ {
+ return false;
+ }
+ else if ((pAnchorListed->GetAnchorId() == RndStdIds::FLY_AT_PAGE) &&
+ (pAnchorNew ->GetAnchorId() == RndStdIds::FLY_AT_PAGE))
+ {
+ return pAnchorListed->GetOrder() < pAnchorNew->GetOrder();
+ }
+
+ // Both objects aren't anchored to page.
+ // Thus, check for to-fly anchored objects
+ if ((pAnchorListed->GetAnchorId() == RndStdIds::FLY_AT_FLY) &&
+ (pAnchorNew ->GetAnchorId() != RndStdIds::FLY_AT_FLY))
+ {
+ return true;
+ }
+ else if ((pAnchorListed->GetAnchorId() != RndStdIds::FLY_AT_FLY) &&
+ (pAnchorNew ->GetAnchorId() == RndStdIds::FLY_AT_FLY))
+ {
+ return false;
+ }
+ else if ((pAnchorListed->GetAnchorId() == RndStdIds::FLY_AT_FLY) &&
+ (pAnchorNew ->GetAnchorId() == RndStdIds::FLY_AT_FLY))
+ {
+ return pAnchorListed->GetOrder() < pAnchorNew->GetOrder();
+ }
+
+ // Both objects aren't anchor to page or to fly
+ // Thus, compare content anchor nodes, if existing.
+ const SwPosition* pContentAnchorListed = pAnchorListed->GetContentAnchor();
+ const SwPosition* pContentAnchorNew = pAnchorNew->GetContentAnchor();
+ if ( pContentAnchorListed && pContentAnchorNew &&
+ pContentAnchorListed->nNode != pContentAnchorNew->nNode )
+ {
+ return pContentAnchorListed->nNode < pContentAnchorNew->nNode;
+ }
+
+ // objects anchored at the same content.
+ // --> OD 2006-11-29 #???# - objects have to be ordered by anchor node position
+ // Thus, compare content anchor node positions and anchor type,
+ // if not anchored at-paragraph
+ if (pContentAnchorListed && pContentAnchorNew)
+ {
+ sal_Int32 nListedIndex = pAnchorListed->GetAnchorId() != RndStdIds::FLY_AT_PARA ?
+ pContentAnchorListed->nContent.GetIndex() : 0;
+ sal_Int32 nNewIndex = pAnchorNew->GetAnchorId() != RndStdIds::FLY_AT_PARA ?
+ pContentAnchorNew->nContent.GetIndex() : 0;
+ if (nListedIndex != nNewIndex)
+ {
+ return nListedIndex < nNewIndex;
+ }
+ }
+
+ int nAnchorListedWeight = GetAnchorWeight(pAnchorListed->GetAnchorId());
+ int nAnchorNewWeight = GetAnchorWeight(pAnchorNew->GetAnchorId());
+ if (nAnchorListedWeight != nAnchorNewWeight)
+ {
+ return nAnchorListedWeight < nAnchorNewWeight;
+ }
+
+ // objects anchored at the same content and at the same content anchor
+ // node position with the same anchor type
+ // Thus, compare its wrapping style including its layer
+ const IDocumentDrawModelAccess& rIDDMA = rFormatListed.getIDocumentDrawModelAccess();
+ const SdrLayerID nHellId = rIDDMA.GetHellId();
+ const SdrLayerID nInvisibleHellId = rIDDMA.GetInvisibleHellId();
+ const bool bWrapThroughOrHellListed =
+ rFormatListed.GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH ||
+ _pListedAnchoredObj->GetDrawObj()->GetLayer() == nHellId ||
+ _pListedAnchoredObj->GetDrawObj()->GetLayer() == nInvisibleHellId;
+ const bool bWrapThroughOrHellNew =
+ rFormatNew.GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH ||
+ _pNewAnchoredObj->GetDrawObj()->GetLayer() == nHellId ||
+ _pNewAnchoredObj->GetDrawObj()->GetLayer() == nInvisibleHellId;
+ if ( bWrapThroughOrHellListed != bWrapThroughOrHellNew )
+ {
+ return !bWrapThroughOrHellListed;
+ }
+ else if ( bWrapThroughOrHellListed && bWrapThroughOrHellNew )
+ {
+ return pAnchorListed->GetOrder() < pAnchorNew->GetOrder();
+ }
+
+ // objects anchored at the same content with a set text wrapping
+ // Thus, compare wrap influences on object position
+ const SwFormatWrapInfluenceOnObjPos* pWrapInfluenceOnObjPosListed =
+ &(rFormatListed.GetWrapInfluenceOnObjPos());
+ const SwFormatWrapInfluenceOnObjPos* pWrapInfluenceOnObjPosNew =
+ &(rFormatNew.GetWrapInfluenceOnObjPos());
+ // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE
+ if ( pWrapInfluenceOnObjPosListed->GetWrapInfluenceOnObjPos( true ) !=
+ pWrapInfluenceOnObjPosNew->GetWrapInfluenceOnObjPos( true ) )
+ {
+ // #i35017# - constant name has changed
+ return pWrapInfluenceOnObjPosListed->GetWrapInfluenceOnObjPos( true )
+ == text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE;
+ }
+
+ // objects anchored at the same content position/page/fly with same
+ // wrap influence.
+ // Thus, compare anchor order number
+ return pAnchorListed->GetOrder() < pAnchorNew->GetOrder();
+ }
+};
+
+}
+
+bool SwSortedObjs::is_sorted() const
+{
+ return std::is_sorted(maSortedObjLst.begin(), maSortedObjLst.end(), ObjAnchorOrder());
+}
+
+bool SwSortedObjs::Insert( SwAnchoredObject& _rAnchoredObj )
+{
+ // #i51941#
+ if ( Contains( _rAnchoredObj ) )
+ {
+ // list already contains object
+ OSL_FAIL( "<SwSortedObjs::Insert()> - already contains object" );
+ return true;
+ }
+
+ // find insert position
+ std::vector< SwAnchoredObject* >::iterator aInsPosIter =
+ std::lower_bound( maSortedObjLst.begin(), maSortedObjLst.end(),
+ &_rAnchoredObj, ObjAnchorOrder() );
+
+ // insert object into list
+ maSortedObjLst.insert( aInsPosIter, &_rAnchoredObj );
+
+ return Contains( _rAnchoredObj );
+}
+
+void SwSortedObjs::Remove( SwAnchoredObject& _rAnchoredObj )
+{
+ std::vector< SwAnchoredObject* >::iterator aDelPosIter =
+ std::find( maSortedObjLst.begin(), maSortedObjLst.end(), &_rAnchoredObj );
+
+ if ( aDelPosIter == maSortedObjLst.end() )
+ {
+ // object not found.
+ OSL_FAIL( "<SwSortedObjs::Remove()> - object not found" );
+ }
+ else
+ {
+ maSortedObjLst.erase( aDelPosIter );
+ }
+}
+
+bool SwSortedObjs::Contains( const SwAnchoredObject& _rAnchoredObj ) const
+{
+ std::vector< SwAnchoredObject* >::const_iterator aIter =
+ std::find( maSortedObjLst.begin(), maSortedObjLst.end(), &_rAnchoredObj );
+
+ return aIter != maSortedObjLst.end();
+}
+
+void SwSortedObjs::Update( SwAnchoredObject& _rAnchoredObj )
+{
+ if ( !Contains( _rAnchoredObj ) )
+ {
+ // given anchored object not found in list
+ OSL_FAIL( "<SwSortedObjs::Update(..) - sorted list doesn't contain given anchored object" );
+ return;
+ }
+
+ if ( size() == 1 )
+ {
+ // given anchored object is the only one in the list.
+ return;
+ }
+
+ Remove( _rAnchoredObj );
+ Insert( _rAnchoredObj );
+}
+
+void SwSortedObjs::UpdateAll()
+{
+ std::stable_sort(maSortedObjLst.begin(), maSortedObjLst.end(), ObjAnchorOrder());
+}
+
+size_t SwSortedObjs::ListPosOf( const SwAnchoredObject& _rAnchoredObj ) const
+{
+ std::vector< SwAnchoredObject* >::const_iterator aIter =
+ std::find( maSortedObjLst.begin(), maSortedObjLst.end(), &_rAnchoredObj );
+
+ if ( aIter != maSortedObjLst.end() )
+ {
+ // #i51941#
+ std::vector< SwAnchoredObject* >::difference_type nPos =
+ aIter - maSortedObjLst.begin();
+ return static_cast<size_t>( nPos );
+ }
+
+ return size();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/ssfrm.cxx b/sw/source/core/layout/ssfrm.cxx
new file mode 100644
index 000000000..c7d11b3ca
--- /dev/null
+++ b/sw/source/core/layout/ssfrm.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <dcontact.hxx>
+#include <flyfrm.hxx>
+#include <txtfrm.hxx>
+#include <cellfrm.hxx>
+#include <swtable.hxx>
+#include <fmtfsize.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <fmtclds.hxx>
+#include <viewimp.hxx>
+#include <sortedobjs.hxx>
+#include <hints.hxx>
+#include <frmtool.hxx>
+#include <ndtxt.hxx>
+#include <osl/diagnose.h>
+
+ // No inline cause we need the function pointers
+tools::Long SwFrame::GetTopMargin() const
+ { return getFramePrintArea().Top(); }
+tools::Long SwFrame::GetBottomMargin() const
+ { return getFrameArea().Height() -getFramePrintArea().Height() -getFramePrintArea().Top(); }
+tools::Long SwFrame::GetLeftMargin() const
+ { return getFramePrintArea().Left(); }
+tools::Long SwFrame::GetRightMargin() const
+ { return getFrameArea().Width() - getFramePrintArea().Width() - getFramePrintArea().Left(); }
+tools::Long SwFrame::GetPrtLeft() const
+ { return getFrameArea().Left() + getFramePrintArea().Left(); }
+tools::Long SwFrame::GetPrtBottom() const
+ { return getFrameArea().Top() + getFramePrintArea().Height() + getFramePrintArea().Top(); }
+tools::Long SwFrame::GetPrtRight() const
+ { return getFrameArea().Left() + getFramePrintArea().Width() + getFramePrintArea().Left(); }
+tools::Long SwFrame::GetPrtTop() const
+ { return getFrameArea().Top() + getFramePrintArea().Top(); }
+
+bool SwFrame::SetMinLeft( tools::Long nDeadline )
+{
+ SwTwips nDiff = nDeadline - getFrameArea().Left();
+ if( nDiff > 0 )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Left( nDeadline );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( aPrt.Width() - nDiff );
+
+ return true;
+ }
+ return false;
+}
+
+bool SwFrame::SetMaxBottom( tools::Long nDeadline )
+{
+ SwTwips nDiff = getFrameArea().Top() + getFrameArea().Height() - nDeadline;
+ if( nDiff > 0 )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( aFrm.Height() - nDiff );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( aPrt.Height() - nDiff );
+
+ return true;
+ }
+ return false;
+}
+
+bool SwFrame::SetMaxRight( tools::Long nDeadline )
+{
+ SwTwips nDiff = getFrameArea().Left() + getFrameArea().Width() - nDeadline;
+ if( nDiff > 0 )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( aFrm.Width() - nDiff );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( aPrt.Width() - nDiff );
+
+ return true;
+ }
+ return false;
+}
+
+void SwFrame::MakeBelowPos( const SwFrame* pUp, const SwFrame* pPrv, bool bNotify )
+{
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if( pPrv )
+ {
+ aFrm.Pos( pPrv->getFrameArea().Pos() );
+ aFrm.Pos().AdjustY(pPrv->getFrameArea().Height() );
+ }
+ else
+ {
+ aFrm.Pos( pUp->getFrameArea().Pos() );
+ aFrm.Pos() += pUp->getFramePrintArea().Pos();
+ }
+
+ if( bNotify )
+ {
+ aFrm.Pos().AdjustY(1 );
+ }
+}
+
+void SwFrame::MakeLeftPos( const SwFrame* pUp, const SwFrame* pPrv, bool bNotify )
+{
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if( pPrv )
+ {
+ aFrm.Pos( pPrv->getFrameArea().Pos() );
+ aFrm.Pos().AdjustX( -(aFrm.Width()) );
+ }
+ else
+ {
+ aFrm.Pos( pUp->getFrameArea().Pos() );
+ aFrm.Pos() += pUp->getFramePrintArea().Pos();
+ aFrm.Pos().AdjustX(pUp->getFramePrintArea().Width() - aFrm.Width() );
+ }
+
+ if( bNotify )
+ {
+ aFrm.Pos().AdjustX( -1 );
+ }
+}
+
+void SwFrame::MakeRightPos( const SwFrame* pUp, const SwFrame* pPrv, bool bNotify )
+{
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if( pPrv )
+ {
+ aFrm.Pos( pPrv->getFrameArea().Pos() );
+ aFrm.Pos().AdjustX(pPrv->getFrameArea().Width() );
+ }
+ else
+ {
+ aFrm.Pos( pUp->getFrameArea().Pos() );
+ aFrm.Pos() += pUp->getFramePrintArea().Pos();
+ }
+
+ if( bNotify )
+ {
+ aFrm.Pos().AdjustX(1 );
+ }
+}
+
+void SwFrame::SetTopBottomMargins( tools::Long nTop, tools::Long nBot )
+{
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Top( nTop );
+ aPrt.Height( getFrameArea().Height() - nTop - nBot );
+}
+
+void SwFrame::SetLeftRightMargins( tools::Long nLeft, tools::Long nRight)
+{
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Left( nLeft );
+ aPrt.Width( getFrameArea().Width() - nLeft - nRight );
+}
+
+void SwFrame::SetRightLeftMargins( tools::Long nRight, tools::Long nLeft)
+{
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Left( nLeft );
+ aPrt.Width( getFrameArea().Width() - nLeft - nRight );
+}
+
+/// checks the layout direction and invalidates the lower frames recursively, if necessary.
+void SwFrame::CheckDirChange()
+{
+ bool bOldVert = mbVertical;
+ bool bOldR2L = mbRightToLeft;
+ SetInvalidVert( true );
+ mbInvalidR2L = true;
+ bool bChg = bOldR2L != IsRightToLeft();
+ bool bOldVertL2R = IsVertLR();
+ if( !(( IsVertical() != bOldVert ) || bChg || bOldVertL2R != IsVertLR()) )
+ return;
+
+ InvalidateAll();
+ if( IsLayoutFrame() )
+ {
+ // set minimum row height for vertical cells in horizontal table:
+ if ( IsCellFrame() && GetUpper() )
+ {
+ if ( IsVertical() != GetUpper()->IsVertical() &&
+ static_cast<SwCellFrame*>(this)->GetTabBox()->getRowSpan() == 1 )
+ {
+ enum {
+ MIN_VERT_CELL_HEIGHT = 1135
+ };
+
+ SwTableLine* pLine = const_cast<SwTableLine*>(static_cast<SwCellFrame*>(this)->GetTabBox()->GetUpper());
+ SwFrameFormat* pFrameFormat = pLine->GetFrameFormat();
+ SwFormatFrameSize aNew( pFrameFormat->GetFrameSize() );
+ if ( SwFrameSize::Fixed != aNew.GetHeightSizeType() )
+ aNew.SetHeightSizeType( SwFrameSize::Minimum );
+ if ( aNew.GetHeight() < MIN_VERT_CELL_HEIGHT )
+ aNew.SetHeight( MIN_VERT_CELL_HEIGHT );
+ SwDoc* pDoc = pFrameFormat->GetDoc();
+ pDoc->SetAttr( aNew, *pLine->ClaimFrameFormat() );
+ }
+ }
+
+ SwFrame* pFrame = static_cast<SwLayoutFrame*>(this)->Lower();
+ const SwFormatCol* pCol = nullptr;
+ SwLayoutFrame* pBody = nullptr;
+ if( pFrame )
+ {
+ if( IsPageFrame() )
+ {
+ // If we're a page frame and we change our layout direction,
+ // we have to look for columns and rearrange them.
+ pBody = static_cast<SwPageFrame*>(this)->FindBodyCont();
+ if(pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame())
+ pCol = &static_cast<SwPageFrame*>(this)->GetFormat()->GetCol();
+ }
+ else if( pFrame->IsColumnFrame() )
+ {
+ pBody = static_cast<SwLayoutFrame*>(this);
+ const SwFrameFormat *pFormat = pBody->GetFormat();
+ if( pFormat )
+ pCol = &pFormat->GetCol();
+ }
+ }
+ while( pFrame )
+ {
+ pFrame->CheckDirChange();
+ pFrame = pFrame->GetNext();
+ }
+ if( pCol )
+ pBody->AdjustColumns( pCol, true );
+ }
+ else if( IsTextFrame() )
+ static_cast<SwTextFrame*>(this)->Prepare();
+
+ // #i31698# - notify anchored objects also for page frames.
+ // Remove code above for special handling of page frames
+ if ( !GetDrawObjs() )
+ return;
+
+ const SwSortedObjs *pObjs = GetDrawObjs();
+ const size_t nCnt = pObjs->size();
+ for ( size_t i = 0; i < nCnt; ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ if( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ pFlyFrame->CheckDirChange();
+ else
+ {
+ // OD 2004-04-06 #i26791# - direct object
+ // positioning no longer needed. Instead
+ // invalidate
+ pAnchoredObj->InvalidateObjPos();
+ }
+ // #i31698# - update layout direction of
+ // anchored object
+ {
+ ::setContextWritingMode( pAnchoredObj->DrawObj(), pAnchoredObj->GetAnchorFrameContainingAnchPos() );
+ pAnchoredObj->UpdateLayoutDir();
+ }
+ }
+}
+
+/// returns the position for anchors based on frame direction
+// OD 2004-03-10 #i11860# - consider lower space and line spacing of
+// previous frame according to new option 'Use former object positioning'
+Point SwFrame::GetFrameAnchorPos( bool bIgnoreFlysAnchoredAtThisFrame ) const
+{
+ Point aAnchor = getFrameArea().Pos();
+
+ if ( ( IsVertical() && !IsVertLR() ) || IsRightToLeft() )
+ aAnchor.AdjustX(getFrameArea().Width() );
+
+ if ( IsTextFrame() )
+ {
+ SwTwips nBaseOfstForFly =
+ static_cast<const SwTextFrame*>(this)->GetBaseOffsetForFly( bIgnoreFlysAnchoredAtThisFrame );
+ if ( IsVertical() )
+ aAnchor.AdjustY(nBaseOfstForFly );
+ else
+ aAnchor.AdjustX(nBaseOfstForFly );
+
+ // OD 2004-03-10 #i11860# - if option 'Use former object positioning'
+ // is OFF, consider the lower space and the line spacing of the
+ // previous frame and the spacing considered for the page grid
+ const SwTextFrame* pThisTextFrame = static_cast<const SwTextFrame*>(this);
+ const SwTwips nUpperSpaceAmountConsideredForPrevFrameAndPageGrid =
+ pThisTextFrame->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ if ( IsVertical() )
+ {
+ aAnchor.AdjustX( -nUpperSpaceAmountConsideredForPrevFrameAndPageGrid );
+ }
+ else
+ {
+ aAnchor.AdjustY(nUpperSpaceAmountConsideredForPrevFrameAndPageGrid );
+ }
+ }
+
+ return aAnchor;
+}
+
+void SwFrame::DestroyImpl()
+{
+ mbInDtor = true;
+
+ // accessible objects for fly and cell frames have been already disposed
+ // by the destructors of the derived classes.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (IsAccessibleFrame() && !(IsFlyFrame() || IsCellFrame())
+ && (GetDep() || IsTextFrame())) // sw_redlinehide: text frame may not have Dep!
+ {
+ assert(!IsTextFrame() || GetDep() || static_cast<SwTextFrame*>(this)->GetMergedPara());
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
+ {
+ SwViewShell *pVSh = pRootFrame->GetCurrShell();
+ if( pVSh && pVSh->Imp() )
+ {
+ OSL_ENSURE( !GetLower(), "Lowers should be dispose already!" );
+ pVSh->Imp()->DisposeAccessibleFrame( this );
+ }
+ }
+ }
+#endif
+
+ if (!m_pDrawObjs)
+ return;
+
+ for (size_t i = m_pDrawObjs->size(); i; )
+ {
+ SwAnchoredObject* pAnchoredObj = (*m_pDrawObjs)[--i];
+ if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ SwFrame::DestroyFrame(pFlyFrame);
+ }
+ else
+ {
+ SdrObject* pSdrObj = pAnchoredObj->DrawObj();
+ SwDrawContact* pContact =
+ static_cast<SwDrawContact*>(pSdrObj->GetUserCall());
+ OSL_ENSURE( pContact,
+ "<SwFrame::~SwFrame> - missing contact for drawing object" );
+ if ( pContact )
+ {
+ pContact->DisconnectObjFromLayout( pSdrObj );
+ }
+ }
+ }
+ m_pDrawObjs.reset();
+}
+
+SwFrame::~SwFrame()
+{
+ assert(m_isInDestroy); // check that only DestroySwFrame does "delete"
+ assert(!IsDeleteForbidden()); // check that it's not deleted while deletes are forbidden
+#if OSL_DEBUG_LEVEL > 0
+ // JP 15.10.2001: for detection of access to deleted frames
+ mpRoot = reinterpret_cast<SwRootFrame*>(0x33333333);
+#endif
+}
+
+void SwFrame::DestroyFrame(SwFrame *const pFrame)
+{
+ if (pFrame)
+ {
+ pFrame->m_isInDestroy = true;
+ pFrame->DestroyImpl();
+ assert(pFrame->mbInDtor); // check that nobody forgot to call base class
+ delete pFrame;
+ }
+}
+
+const SwFrameFormat * SwLayoutFrame::GetFormat() const
+{
+ return static_cast< const SwFrameFormat * >( GetDep() );
+}
+
+SwFrameFormat * SwLayoutFrame::GetFormat()
+{
+ return static_cast< SwFrameFormat * >( GetDep() );
+}
+
+void SwLayoutFrame::SetFrameFormat(SwFrameFormat* pNew)
+{
+ if(pNew == GetFormat())
+ return;
+ const SwFormatChg aOldFormat(GetFormat());
+ pNew->Add(this);
+ const SwFormatChg aNewFormat(pNew);
+ SwClientNotify(*pNew, sw::LegacyModifyHint(&aOldFormat, &aNewFormat));
+}
+
+SwContentFrame::SwContentFrame( SwContentNode * const pContent, SwFrame* pSib ) :
+ SwFrame( pContent, pSib ),
+ SwFlowFrame( static_cast<SwFrame&>(*this) )
+{
+ assert(!getRootFrame()->HasMergedParas() || pContent->IsCreateFrameWhenHidingRedlines());
+}
+
+void SwContentFrame::DestroyImpl()
+{
+ const SwContentNode* pCNd(dynamic_cast<SwContentNode*>(GetDep()));
+ if (nullptr == pCNd && IsTextFrame())
+ {
+ pCNd = static_cast<SwTextFrame*>(this)->GetTextNodeFirst();
+ }
+ // IsInDtor shouldn't be happening with ViewShell owning layout
+ assert(nullptr == pCNd || !pCNd->GetDoc().IsInDtor());
+ if (nullptr != pCNd && !pCNd->GetDoc().IsInDtor())
+ {
+ //Unregister from root if I'm still in turbo there.
+ SwRootFrame *pRoot = getRootFrame();
+ if( pRoot && pRoot->GetTurbo() == this )
+ {
+ pRoot->DisallowTurbo();
+ pRoot->ResetTurbo();
+ }
+ }
+
+ SwFrame::DestroyImpl();
+}
+
+SwContentFrame::~SwContentFrame()
+{
+}
+
+void SwTextFrame::RegisterToNode(SwTextNode & rNode, bool const isForceNodeAsFirst)
+{
+ if (isForceNodeAsFirst && m_pMergedPara)
+ { // nothing registered here, in particular no delete redlines (insert
+ // redline might end on empty node where delete rl starts, should be ok)
+ assert(m_pMergedPara->pFirstNode->GetIndex() + 1 == rNode.GetIndex());
+ assert(rNode.GetDoc().getIDocumentRedlineAccess().GetRedlinePos(
+ *m_pMergedPara->pFirstNode, RedlineType::Delete) == SwRedlineTable::npos);
+ assert(std::find_if(
+ rNode.GetDoc().getIDocumentMarkAccess()->getFieldmarksBegin(),
+ rNode.GetDoc().getIDocumentMarkAccess()->getFieldmarksEnd(),
+ [this](::sw::mark::IMark const*const pMark) {
+ return pMark->GetMarkStart().nNode == *m_pMergedPara->pFirstNode
+ && pMark->GetMarkEnd().nNode != *m_pMergedPara->pFirstNode;
+ }) == rNode.GetDoc().getIDocumentMarkAccess()->getFieldmarksEnd());
+ }
+ assert(&rNode != GetDep());
+ assert(!m_pMergedPara
+ || (m_pMergedPara->pFirstNode->GetIndex() < rNode.GetIndex())
+ || (rNode.GetIndex() + 1 == m_pMergedPara->pFirstNode->GetIndex()));
+ SwTextNode & rFirstNode(
+ (!isForceNodeAsFirst && m_pMergedPara && m_pMergedPara->pFirstNode->GetIndex() < rNode.GetIndex())
+ ? *m_pMergedPara->pFirstNode
+ : rNode);
+ // sw_redlinehide: use New here, because the only caller also calls lcl_ChangeFootnoteRef
+ m_pMergedPara = sw::CheckParaRedlineMerge(*this, rFirstNode, sw::FrameMode::New);
+ if (!m_pMergedPara)
+ {
+ rNode.Add(this);
+ }
+}
+
+void SwLayoutFrame::DestroyImpl()
+{
+ while (!m_VertPosOrientFramesFor.empty())
+ {
+ SwAnchoredObject *pObj = *m_VertPosOrientFramesFor.begin();
+ pObj->ClearVertPosOrientFrame();
+ }
+
+ assert(m_VertPosOrientFramesFor.empty());
+
+ SwFrame *pFrame = m_pLower;
+
+ if( GetFormat() && !GetFormat()->GetDoc()->IsInDtor() )
+ {
+ while ( pFrame )
+ {
+ //First delete the Objs of the Frame because they can't unregister
+ //from the page after remove.
+ //We don't want to create an endless loop only because one couldn't
+ //unregister.
+
+ while ( pFrame->GetDrawObjs() && pFrame->GetDrawObjs()->size() )
+ {
+ const size_t nCnt = pFrame->GetDrawObjs()->size();
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[0];
+ if (SwFlyFrame* pFlyFrame = pAnchoredObj->DynCastFlyFrame())
+ {
+ SwFrame::DestroyFrame(pFlyFrame);
+ assert(!pFrame->GetDrawObjs() || nCnt > pFrame->GetDrawObjs()->size());
+ }
+ else
+ {
+ pAnchoredObj->ClearTmpConsiderWrapInfluence();
+ SdrObject* pSdrObj = pAnchoredObj->DrawObj();
+ SwDrawContact* pContact =
+ static_cast<SwDrawContact*>(pSdrObj->GetUserCall());
+ OSL_ENSURE( pContact,
+ "<SwFrame::~SwFrame> - missing contact for drawing object" );
+ if ( pContact )
+ {
+ pContact->DisconnectObjFromLayout( pSdrObj );
+ }
+
+ if ( pFrame->GetDrawObjs() &&
+ nCnt == pFrame->GetDrawObjs()->size() )
+ {
+ pFrame->GetDrawObjs()->Remove( *pAnchoredObj );
+ }
+ }
+ }
+ pFrame->RemoveFromLayout();
+ SwFrame::DestroyFrame(pFrame);
+ pFrame = m_pLower;
+ }
+ //Delete the Flys, the last one also deletes the array.
+ while ( GetDrawObjs() && GetDrawObjs()->size() )
+ {
+ const size_t nCnt = GetDrawObjs()->size();
+
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[0];
+ if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ SwFrame::DestroyFrame(pFlyFrame);
+ assert(!GetDrawObjs() || nCnt > GetDrawObjs()->size());
+ }
+ else
+ {
+ SdrObject* pSdrObj = pAnchoredObj->DrawObj();
+ SwDrawContact* pContact =
+ static_cast<SwDrawContact*>(pSdrObj->GetUserCall());
+ OSL_ENSURE( pContact,
+ "<SwFrame::~SwFrame> - missing contact for drawing object" );
+ if ( pContact )
+ {
+ pContact->DisconnectObjFromLayout( pSdrObj );
+ }
+
+ if ( GetDrawObjs() && nCnt == GetDrawObjs()->size() )
+ {
+ GetDrawObjs()->Remove( *pAnchoredObj );
+ }
+ }
+ }
+ }
+ else
+ {
+ while( pFrame )
+ {
+ SwFrame *pNxt = pFrame->GetNext();
+ SwFrame::DestroyFrame(pFrame);
+ pFrame = pNxt;
+ }
+ }
+
+ SwFrame::DestroyImpl();
+}
+
+SwLayoutFrame::~SwLayoutFrame()
+{
+}
+
+/**
+|* The paintarea is the area, in which the content of a frame is allowed
+|* to be displayed. This region could be larger than the printarea (getFramePrintArea())
+|* of the upper, it includes e.g. often the margin of the page.
+|*/
+SwRect SwFrame::GetPaintArea() const
+{
+ // NEW TABLES
+ // Cell frames may not leave their upper:
+ SwRect aRect = IsRowFrame() ? GetUpper()->getFrameArea() : getFrameArea();
+ const bool bVert = IsVertical();
+ SwRectFn fnRect = bVert ? ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nRight = (aRect.*fnRect->fnGetRight)();
+ tools::Long nLeft = (aRect.*fnRect->fnGetLeft)();
+ const SwFrame* pTmp = this;
+ bool bLeft = true;
+ bool bRight = true;
+ tools::Long nRowSpan = 0;
+ while( pTmp )
+ {
+ if( pTmp->IsCellFrame() && pTmp->GetUpper() &&
+ pTmp->GetUpper()->IsVertical() != pTmp->IsVertical() )
+ nRowSpan = static_cast<const SwCellFrame*>(pTmp)->GetTabBox()->getRowSpan();
+ tools::Long nTmpRight = (pTmp->getFrameArea().*fnRect->fnGetRight)();
+ tools::Long nTmpLeft = (pTmp->getFrameArea().*fnRect->fnGetLeft)();
+ if( pTmp->IsRowFrame() && nRowSpan > 1 )
+ {
+ const SwFrame* pNxt = pTmp;
+ while( --nRowSpan > 0 && pNxt->GetNext() )
+ pNxt = pNxt->GetNext();
+ if( pTmp->IsVertical() )
+ nTmpLeft = (pNxt->getFrameArea().*fnRect->fnGetLeft)();
+ else
+ {
+ // pTmp is a row frame, but it's not vertical.
+ if (IsVertLRBT())
+ {
+ // This frame cell is OK to expand towards the physical down direction.
+ // Physical down is left.
+ nTmpLeft = (pNxt->getFrameArea().*fnRect->fnGetLeft)();
+ }
+ else
+ {
+ nTmpRight = (pNxt->getFrameArea().*fnRect->fnGetRight)();
+ }
+ }
+ }
+ OSL_ENSURE( pTmp, "GetPaintArea lost in time and space" );
+ if( pTmp->IsPageFrame() || pTmp->IsFlyFrame() ||
+ pTmp->IsCellFrame() || pTmp->IsRowFrame() || //nobody leaves a table!
+ pTmp->IsRootFrame() )
+ {
+ // BTLR is OK to expand towards the physical down direction. Physical down is left.
+ if( bLeft || (aRectFnSet.XDiff(nTmpLeft, nLeft) > 0 && !IsVertLRBT()) )
+ nLeft = nTmpLeft;
+ if( bRight || aRectFnSet.XDiff(nRight, nTmpRight) > 0 )
+ nRight = nTmpRight;
+ if( pTmp->IsPageFrame() || pTmp->IsFlyFrame() || pTmp->IsRootFrame() )
+ break;
+ bLeft = false;
+ bRight = false;
+ }
+ else if( pTmp->IsColumnFrame() ) // nobody enters neighbour columns
+ {
+ bool bR2L = pTmp->IsRightToLeft();
+ // the first column has _no_ influence to the left range
+ if( bR2L ? pTmp->GetNext() : pTmp->GetPrev() )
+ {
+ if( bLeft || aRectFnSet.XDiff(nTmpLeft, nLeft) > 0 )
+ nLeft = nTmpLeft;
+ bLeft = false;
+ }
+ // the last column has _no_ influence to the right range
+ if( bR2L ? pTmp->GetPrev() : pTmp->GetNext() )
+ {
+ if( bRight || aRectFnSet.XDiff(nRight, nTmpRight) > 0 )
+ nRight = nTmpRight;
+ bRight = false;
+ }
+ }
+ else if( bVert && pTmp->IsBodyFrame() )
+ {
+ // Header and footer frames have always horizontal direction and
+ // limit the body frame.
+ // A previous frame of a body frame must be a header,
+ // the next frame of a body frame may be a footnotecontainer or
+ // a footer. The footnotecontainer has the same direction like
+ // the body frame.
+ if( pTmp->GetPrev() && ( bLeft || aRectFnSet.XDiff(nTmpLeft, nLeft) > 0 ) )
+ {
+ nLeft = nTmpLeft;
+ bLeft = false;
+ }
+ if( pTmp->GetNext() &&
+ ( pTmp->GetNext()->IsFooterFrame() || pTmp->GetNext()->GetNext() )
+ && ( bRight || aRectFnSet.XDiff(nRight, nTmpRight) > 0 ) )
+ {
+ nRight = nTmpRight;
+ bRight = false;
+ }
+ }
+ pTmp = pTmp->GetUpper();
+ }
+ (aRect.*fnRect->fnSetLeft)( nLeft );
+ (aRect.*fnRect->fnSetRight)( nRight );
+ return aRect;
+}
+
+/**
+|* The unionframe is the framearea (getFrameArea()) of a frame expanded by the
+|* printarea, if there's a negative margin at the left or right side.
+|*/
+SwRect SwFrame::UnionFrame( bool bBorder ) const
+{
+ bool bVert = IsVertical();
+ SwRectFn fnRect = bVert ? ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+ tools::Long nLeft = (getFrameArea().*fnRect->fnGetLeft)();
+ tools::Long nWidth = (getFrameArea().*fnRect->fnGetWidth)();
+ tools::Long nPrtLeft = (getFramePrintArea().*fnRect->fnGetLeft)();
+ tools::Long nPrtWidth = (getFramePrintArea().*fnRect->fnGetWidth)();
+ SwRectFnSet aRectFnSet(this);
+ if (aRectFnSet.XInc(nPrtLeft, nPrtWidth) > nWidth)
+ nWidth = nPrtLeft + nPrtWidth;
+ if( nPrtLeft < 0 )
+ {
+ nLeft += nPrtLeft;
+ nWidth -= nPrtLeft;
+ }
+ SwTwips nRight = aRectFnSet.XInc(nLeft, nWidth);
+ tools::Long nAdd = 0;
+ if( bBorder )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ const SvxBoxItem &rBox = rAttrs.GetBox();
+ if ( rBox.GetLeft() )
+ nLeft -= rBox.CalcLineSpace( SvxBoxItemLine::LEFT );
+ else
+ nLeft -= rBox.GetDistance( SvxBoxItemLine::LEFT ) + 1;
+ if ( rBox.GetRight() )
+ nAdd += rBox.CalcLineSpace( SvxBoxItemLine::RIGHT );
+ else
+ nAdd += rBox.GetDistance( SvxBoxItemLine::RIGHT ) + 1;
+ if( rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
+ {
+ const SvxShadowItem &rShadow = rAttrs.GetShadow();
+ nLeft -= rShadow.CalcShadowSpace( SvxShadowItemSide::LEFT );
+ nAdd += rShadow.CalcShadowSpace( SvxShadowItemSide::RIGHT );
+ }
+ }
+ if( IsTextFrame() && static_cast<const SwTextFrame*>(this)->HasPara() )
+ {
+ tools::Long nTmp = static_cast<const SwTextFrame*>(this)->HangingMargin();
+ if( nTmp > nAdd )
+ nAdd = nTmp;
+ }
+ nWidth = aRectFnSet.XDiff(aRectFnSet.XInc(nRight, nAdd), nLeft);
+ SwRect aRet( getFrameArea() );
+ (aRet.*fnRect->fnSetLeft)(nLeft);
+ (aRet.*fnRect->fnSetWidth)( nWidth );
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/swselectionlist.cxx b/sw/source/core/layout/swselectionlist.cxx
new file mode 100644
index 000000000..b7628cbac
--- /dev/null
+++ b/sw/source/core/layout/swselectionlist.cxx
@@ -0,0 +1,83 @@
+/* -*- 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 <swselectionlist.hxx>
+#include <flyfrm.hxx>
+#include <ftnfrm.hxx>
+
+/** This class is used as parameter for functions to create a rectangular text selection
+*/
+
+namespace {
+
+ /** Find the context of a given frame
+
+ A context is the environment where text is allowed to flow.
+ The context is represented by
+ - the SwRootFrame if the frame is part of a page body
+ - the SwHeaderFrame if the frame is part of a page header
+ - the SwFooterFrame if the frame is part of a page footer
+ - the (master) SwFootnoteFrame if the frame is part of footnote
+ - the (first) SwFlyFrame if the frame is part of a (linked) fly frame
+
+ @param pFrame
+ the given frame
+
+ @return the context of the frame, represented by a SwFrame*
+ */
+ const SwFrame* getContext( const SwFrame* pFrame )
+ {
+ while( pFrame )
+ {
+ if( pFrame->IsRootFrame() || pFrame->IsHeaderFrame() || pFrame->IsFooterFrame() )
+ break;
+ if( pFrame->IsFlyFrame() )
+ {
+ const SwFlyFrame* pFly = static_cast<const SwFlyFrame*>( pFrame );
+ while( pFly->GetPrevLink() )
+ pFly = pFly->GetPrevLink();
+ break;
+ }
+ if( pFrame->IsFootnoteFrame() )
+ {
+ const SwFootnoteFrame* pFootnote = static_cast<const SwFootnoteFrame*>( pFrame );
+ while( pFootnote->GetMaster() )
+ pFootnote = pFootnote->GetMaster();
+ break;
+ }
+ pFrame = pFrame->GetUpper();
+ }
+ return pFrame;
+ }
+}
+
+SwSelectionList::SwSelectionList( const SwFrame* pInitCxt ) :
+ m_pContext( getContext( pInitCxt ) )
+{
+}
+
+bool SwSelectionList::checkContext( const SwFrame* pCheck )
+{
+ pCheck = getContext( pCheck );
+ if( !m_pContext )
+ m_pContext = pCheck;
+ return m_pContext == pCheck;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
new file mode 100644
index 000000000..8e3a1b5dc
--- /dev/null
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -0,0 +1,6170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <viewimp.hxx>
+#include <fesh.hxx>
+#include <swtable.hxx>
+#include <deletelistener.hxx>
+#include <dflyobj.hxx>
+#include <anchoreddrawobject.hxx>
+#include <fmtanchr.hxx>
+#include <viewopt.hxx>
+#include <hints.hxx>
+#include <dbg_lay.hxx>
+#include <ftnidx.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <basegfx/range/b1drange.hxx>
+#include <fmtlsplt.hxx>
+#include <fmtrowsplt.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtornt.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtfsize.hxx>
+#include <swtblfmt.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <flyfrms.hxx>
+#include <txtfrm.hxx>
+#include <ftnfrm.hxx>
+#include <notxtfrm.hxx>
+#include <htmltbl.hxx>
+#include <sectfrm.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <sortedobjs.hxx>
+#include <objectformatter.hxx>
+#include <layouter.hxx>
+#include <calbck.hxx>
+#include <DocumentSettingManager.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <ndtxt.hxx>
+#include <frameformats.hxx>
+
+using namespace ::com::sun::star;
+
+SwTabFrame::SwTabFrame( SwTable &rTab, SwFrame* pSib )
+ : SwLayoutFrame( rTab.GetFrameFormat(), pSib )
+ , SwFlowFrame( static_cast<SwFrame&>(*this) )
+ , m_pTable( &rTab )
+ , m_bComplete(false)
+ , m_bCalcLowers(false)
+ , m_bLowersFormatted(false)
+ , m_bLockBackMove(false)
+ , m_bResizeHTMLTable(false)
+ , m_bONECalcLowers(false)
+ , m_bHasFollowFlowLine(false)
+ , m_bIsRebuildLastLine(false)
+ , m_bRestrictTableGrowth(false)
+ , m_bRemoveFollowFlowLinePending(false)
+ , m_bConsiderObjsForMinCellHeight(true)
+ , m_bObjsDoesFit(true)
+ , m_bInRecalcLowerRow(false)
+{
+ mbFixSize = false; //Don't fall for import filter again.
+ mnFrameType = SwFrameType::Tab;
+
+ //Create the lines and insert them.
+ const SwTableLines &rLines = rTab.GetTabLines();
+ SwFrame *pTmpPrev = nullptr;
+ bool bHiddenRedlines = getRootFrame()->IsHideRedlines() &&
+ !GetFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().empty();
+ SwRedlineTable::size_type nRedlinePos = 0;
+ for ( size_t i = 0; i < rLines.size(); ++i )
+ {
+ // skip lines deleted with track changes
+ if ( bHiddenRedlines && rLines[i]->IsDeleted(nRedlinePos) )
+ continue;
+
+ SwRowFrame *pNew = new SwRowFrame( *rLines[i], this );
+ if( pNew->Lower() )
+ {
+ pNew->InsertBehind( this, pTmpPrev );
+ pTmpPrev = pNew;
+ }
+ else
+ SwFrame::DestroyFrame(pNew);
+ }
+ OSL_ENSURE( Lower() && Lower()->IsRowFrame(), "SwTabFrame::SwTabFrame: No rows." );
+}
+
+SwTabFrame::SwTabFrame( SwTabFrame &rTab )
+ : SwLayoutFrame( rTab.GetFormat(), &rTab )
+ , SwFlowFrame( static_cast<SwFrame&>(*this) )
+ , m_pTable( rTab.GetTable() )
+ , m_bComplete(false)
+ , m_bCalcLowers(false)
+ , m_bLowersFormatted(false)
+ , m_bLockBackMove(false)
+ , m_bResizeHTMLTable(false)
+ , m_bONECalcLowers(false)
+ , m_bHasFollowFlowLine(false)
+ , m_bIsRebuildLastLine(false)
+ , m_bRestrictTableGrowth(false)
+ , m_bRemoveFollowFlowLinePending(false)
+ , m_bConsiderObjsForMinCellHeight(true)
+ , m_bObjsDoesFit(true)
+ , m_bInRecalcLowerRow(false)
+{
+ mbFixSize = false; //Don't fall for import filter again.
+ mnFrameType = SwFrameType::Tab;
+
+ SetFollow( rTab.GetFollow() );
+ rTab.SetFollow( this );
+}
+
+void SwTabFrame::DestroyImpl()
+{
+ // There is some terrible code in fetab.cxx, that
+ // caches pointers to SwTabFrames.
+ ::ClearFEShellTabCols(*GetFormat()->GetDoc(), this);
+
+ SwLayoutFrame::DestroyImpl();
+}
+
+SwTabFrame::~SwTabFrame()
+{
+}
+
+void SwTabFrame::JoinAndDelFollows()
+{
+ SwTabFrame *pFoll = GetFollow();
+ if ( pFoll->HasFollow() )
+ pFoll->JoinAndDelFollows();
+ pFoll->Cut();
+ SetFollow( pFoll->GetFollow() );
+ SwFrame::DestroyFrame(pFoll);
+}
+
+void SwTabFrame::RegistFlys()
+{
+ OSL_ENSURE( Lower() && Lower()->IsRowFrame(), "No rows." );
+
+ SwPageFrame *pPage = FindPageFrame();
+ if ( pPage )
+ {
+ SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
+ do
+ {
+ pRow->RegistFlys( pPage );
+ pRow = static_cast<SwRowFrame*>(pRow->GetNext());
+ } while ( pRow );
+ }
+}
+
+static void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom );
+static void lcl_RecalcRow( SwRowFrame& rRow, tools::Long nBottom );
+static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva );
+// #i26945# - add parameter <_bOnlyRowsAndCells> to control
+// that only row and cell frames are formatted.
+static bool lcl_InnerCalcLayout( SwFrame *pFrame,
+ tools::Long nBottom,
+ bool _bOnlyRowsAndCells = false );
+// OD 2004-02-18 #106629# - correct type of 1st parameter
+// #i26945# - add parameter <_bConsiderObjs> in order to
+// control, if floating screen objects have to be considered for the minimal
+// cell height.
+static SwTwips lcl_CalcMinRowHeight( const SwRowFrame *pRow,
+ const bool _bConsiderObjs );
+static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame&, const SwBorderAttrs& );
+
+static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow);
+
+static SwTwips lcl_GetHeightOfRows( const SwFrame* pStart, tools::Long nCount )
+{
+ if ( !nCount || !pStart)
+ return 0;
+
+ SwTwips nRet = 0;
+ SwRectFnSet aRectFnSet(pStart);
+ while ( pStart && nCount > 0 )
+ {
+ nRet += aRectFnSet.GetHeight(pStart->getFrameArea());
+ pStart = pStart->GetNext();
+ --nCount;
+ }
+
+ return nRet;
+}
+
+// Local helper function to insert a new follow flow line
+static SwRowFrame* lcl_InsertNewFollowFlowLine( SwTabFrame& rTab, const SwFrame& rTmpRow, bool bRowSpanLine )
+{
+ OSL_ENSURE( rTmpRow.IsRowFrame(), "No row frame to copy for FollowFlowLine" );
+ const SwRowFrame& rRow = static_cast<const SwRowFrame&>(rTmpRow);
+
+ rTab.SetFollowFlowLine( true );
+ SwRowFrame *pFollowFlowLine = new SwRowFrame(*rRow.GetTabLine(), &rTab, false );
+ pFollowFlowLine->SetRowSpanLine( bRowSpanLine );
+ SwFrame* pFirstRow = rTab.GetFollow()->GetFirstNonHeadlineRow();
+ pFollowFlowLine->InsertBefore( rTab.GetFollow(), pFirstRow );
+ return pFollowFlowLine;
+}
+
+// #i26945# - local helper function to invalidate all lower
+// objects. By parameter <_bMoveObjsOutOfRange> it can be controlled, if
+// additionally the objects are moved 'out of range'.
+static void lcl_InvalidateLowerObjs( SwLayoutFrame& _rLayoutFrame,
+ const bool _bMoveObjsOutOfRange = false,
+ SwPageFrame* _pPageFrame = nullptr )
+{
+ // determine page frame, if needed
+ if ( !_pPageFrame )
+ {
+ _pPageFrame = _rLayoutFrame.FindPageFrame();
+ OSL_ENSURE( _pPageFrame,
+ "<lcl_InvalidateLowerObjs(..)> - missing page frame -> no move of lower objects out of range" );
+ if ( !_pPageFrame )
+ {
+ return;
+ }
+ }
+
+ // loop on lower frames
+ SwFrame* pLowerFrame = _rLayoutFrame.Lower();
+ while ( pLowerFrame )
+ {
+ if ( pLowerFrame->IsLayoutFrame() )
+ {
+ ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pLowerFrame),
+ _bMoveObjsOutOfRange, _pPageFrame );
+ }
+ if ( pLowerFrame->GetDrawObjs() )
+ {
+ for (size_t i = 0, nCount = pLowerFrame->GetDrawObjs()->size(); i < nCount; ++i)
+ {
+ SwAnchoredObject* pAnchoredObj = (*pLowerFrame->GetDrawObjs())[i];
+
+ // invalidate position of anchored object
+ pAnchoredObj->SetTmpConsiderWrapInfluence( false );
+ pAnchoredObj->SetConsiderForTextWrap( false );
+ pAnchoredObj->UnlockPosition();
+ pAnchoredObj->InvalidateObjPos();
+
+ SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
+
+ // move anchored object 'out of range'
+ if ( _bMoveObjsOutOfRange )
+ {
+ // indicate, that positioning is progress to avoid
+ // modification of the anchored object resp. it's attributes
+ // due to the movement
+ SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
+ pAnchoredObj->SetObjLeft( _pPageFrame->getFrameArea().Right() );
+ // #115759# - reset character rectangle,
+ // top of line and relative position in order to assure,
+ // that anchored object is correctly positioned.
+ pAnchoredObj->ClearCharRectAndTopOfLine();
+ pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
+ if ( pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ == RndStdIds::FLY_AS_CHAR )
+ {
+ pAnchoredObj->AnchorFrame()
+ ->Prepare( PrepareHint::FlyFrameAttributesChanged,
+ &(pAnchoredObj->GetFrameFormat()) );
+ }
+ if ( pFly != nullptr )
+ {
+ pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
+ pFly->GetVirtDrawObj()->SetChanged();
+ }
+ }
+
+ // If anchored object is a fly frame, invalidate its lower objects
+ if ( pFly != nullptr )
+ {
+ ::lcl_InvalidateLowerObjs( *pFly, _bMoveObjsOutOfRange, _pPageFrame );
+ }
+ }
+ }
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+}
+
+// Local helper function to shrink all lowers of pRow to 0 height
+static void lcl_ShrinkCellsAndAllContent( SwRowFrame& rRow )
+{
+ SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rRow.Lower());
+ SwRectFnSet aRectFnSet(pCurrMasterCell);
+
+ bool bAllCellsCollapsed = true;
+ while ( pCurrMasterCell )
+ {
+ // NEW TABLES
+ SwCellFrame& rToAdjust = pCurrMasterCell->GetTabBox()->getRowSpan() < 1 ?
+ const_cast<SwCellFrame&>(pCurrMasterCell->FindStartEndOfRowSpanCell( true )) :
+ *pCurrMasterCell;
+
+ // #i26945#
+ // all lowers should have the correct position
+ lcl_ArrangeLowers( &rToAdjust,
+ aRectFnSet.GetPrtTop(rToAdjust),
+ false );
+ // TODO: Optimize number of frames which are set to 0 height
+ // we have to start with the last lower frame, otherwise
+ // the shrink will not shrink the current cell
+ SwFrame* pTmp = rToAdjust.GetLastLower();
+ bool bAllLowersCollapsed = true;
+
+ if ( pTmp && pTmp->IsRowFrame() )
+ {
+ SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pTmp);
+ lcl_ShrinkCellsAndAllContent( *pTmpRow );
+ }
+ else
+ {
+ // TODO: Optimize number of frames which are set to 0 height
+ while ( pTmp )
+ {
+ // the frames have to be shrunk
+ if ( pTmp->IsTabFrame() )
+ {
+ SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(static_cast<SwTabFrame*>(pTmp)->Lower());
+ bool bAllRowsCollapsed = true;
+
+ while ( pTmpRow )
+ {
+ lcl_ShrinkCellsAndAllContent( *pTmpRow );
+
+ if (aRectFnSet.GetHeight(pTmpRow->getFrameArea()) > 0)
+ bAllRowsCollapsed = false;
+
+ pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
+ }
+
+ if (bAllRowsCollapsed)
+ {
+ // All rows of this table have 0 height -> set height of the table itself as well.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pTmp);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
+ aRectFnSet.SetTop(aPrt, 0);
+ aRectFnSet.SetHeight(aPrt, 0);
+ }
+ else
+ bAllLowersCollapsed = false;
+ }
+ else
+ {
+ pTmp->Shrink(aRectFnSet.GetHeight(pTmp->getFrameArea()));
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
+ aRectFnSet.SetTop(aPrt, 0);
+ aRectFnSet.SetHeight(aPrt, 0);
+
+ if (aRectFnSet.GetHeight(pTmp->getFrameArea()) > 0)
+ {
+ bAllLowersCollapsed = false;
+ }
+ }
+
+ pTmp = pTmp->GetPrev();
+ }
+
+ // all lowers should have the correct position
+ lcl_ArrangeLowers( &rToAdjust,
+ aRectFnSet.GetPrtTop(rToAdjust),
+ false );
+ }
+
+ if (bAllLowersCollapsed)
+ {
+ // All lower frame of this cell have 0 height -> set height of the cell itself as well.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCurrMasterCell);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pCurrMasterCell);
+ aRectFnSet.SetTop(aPrt, 0);
+ aRectFnSet.SetHeight(aPrt, 0);
+ }
+ else
+ bAllCellsCollapsed = false;
+
+ pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
+ }
+
+ if (bAllCellsCollapsed)
+ {
+ // All cells have 0 height -> set height of row as well.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(rRow);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(rRow);
+ aRectFnSet.SetTop(aPrt, 0);
+ aRectFnSet.SetHeight(aPrt, 0);
+ }
+}
+
+// Local helper function to move the content from rSourceLine to rDestLine
+// The content is inserted behind the last content in the corresponding
+// cell in rDestLine.
+static void lcl_MoveRowContent( SwRowFrame& rSourceLine, SwRowFrame& rDestLine )
+{
+ SwCellFrame* pCurrDestCell = static_cast<SwCellFrame*>(rDestLine.Lower());
+ SwCellFrame* pCurrSourceCell = static_cast<SwCellFrame*>(rSourceLine.Lower());
+
+ // Move content of follow cells into master cells
+ while ( pCurrSourceCell )
+ {
+ if ( pCurrSourceCell->Lower() && pCurrSourceCell->Lower()->IsRowFrame() )
+ {
+ SwRowFrame* pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
+ while ( pTmpSourceRow )
+ {
+ // #125926# Attention! It is possible,
+ // that pTmpSourceRow->IsFollowFlowRow() but pTmpDestRow
+ // cannot be found. In this case, we have to move the complete
+ // row.
+ SwRowFrame* pTmpDestRow = static_cast<SwRowFrame*>(pCurrDestCell->Lower());
+
+ if ( pTmpSourceRow->IsFollowFlowRow() && pTmpDestRow )
+ {
+ // move content from follow flow row to pTmpDestRow:
+ while ( pTmpDestRow->GetNext() )
+ pTmpDestRow = static_cast<SwRowFrame*>(pTmpDestRow->GetNext());
+
+ assert(pTmpDestRow->GetFollowRow() == pTmpSourceRow);
+
+ lcl_MoveRowContent( *pTmpSourceRow, *pTmpDestRow );
+ pTmpDestRow->SetFollowRow( pTmpSourceRow->GetFollowRow() );
+ pTmpSourceRow->RemoveFromLayout();
+ SwFrame::DestroyFrame(pTmpSourceRow);
+ }
+ else
+ {
+ // move complete row:
+ pTmpSourceRow->RemoveFromLayout();
+ pTmpSourceRow->InsertBefore( pCurrDestCell, nullptr );
+ }
+
+ pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
+ }
+ }
+ else
+ {
+ SwFrame *pTmp = ::SaveContent( pCurrSourceCell );
+ if ( pTmp )
+ {
+ // NEW TABLES
+ SwCellFrame* pDestCell = pCurrDestCell;
+ if ( pDestCell->GetTabBox()->getRowSpan() < 1 )
+ pDestCell = & const_cast<SwCellFrame&>(pDestCell->FindStartEndOfRowSpanCell( true ));
+
+ // Find last content
+ SwFrame* pFrame = pDestCell->GetLastLower();
+ ::RestoreContent( pTmp, pDestCell, pFrame );
+ }
+ }
+ pCurrDestCell = static_cast<SwCellFrame*>(pCurrDestCell->GetNext());
+ pCurrSourceCell = static_cast<SwCellFrame*>(pCurrSourceCell->GetNext());
+ }
+}
+
+// Local helper function to move all footnotes in rRowFrame from
+// the footnote boss of rSource to the footnote boss of rDest.
+static void lcl_MoveFootnotes( SwTabFrame& rSource, SwTabFrame& rDest, SwLayoutFrame& rRowFrame )
+{
+ if ( !rSource.GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
+ {
+ SwFootnoteBossFrame* pOldBoss = rSource.FindFootnoteBossFrame( true );
+ SwFootnoteBossFrame* pNewBoss = rDest.FindFootnoteBossFrame( true );
+ rRowFrame.MoveLowerFootnotes( nullptr, pOldBoss, pNewBoss, true );
+ }
+}
+
+// Local helper function to handle nested table cells before the split process
+static void lcl_PreprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine,
+ SwRowFrame& rFollowFlowLine, SwTwips nRemain )
+{
+ SwCellFrame* pCurrLastLineCell = static_cast<SwCellFrame*>(rLastLine.Lower());
+ SwCellFrame* pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(rFollowFlowLine.Lower());
+
+ SwRectFnSet aRectFnSet(pCurrLastLineCell);
+
+ // Move content of follow cells into master cells
+ while ( pCurrLastLineCell )
+ {
+ if ( pCurrLastLineCell->Lower() && pCurrLastLineCell->Lower()->IsRowFrame() )
+ {
+ SwTwips nTmpCut = nRemain;
+ SwRowFrame* pTmpLastLineRow = static_cast<SwRowFrame*>(pCurrLastLineCell->Lower());
+
+ // #i26945#
+ SwTwips nCurrentHeight =
+ lcl_CalcMinRowHeight( pTmpLastLineRow,
+ rTab.IsConsiderObjsForMinCellHeight() );
+ while ( pTmpLastLineRow->GetNext() && nTmpCut > nCurrentHeight )
+ {
+ nTmpCut -= nCurrentHeight;
+ pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
+ // #i26945#
+ nCurrentHeight =
+ lcl_CalcMinRowHeight( pTmpLastLineRow,
+ rTab.IsConsiderObjsForMinCellHeight() );
+ }
+
+ // pTmpLastLineRow does not fit to the line or it is the last line
+ // Check if we can move pTmpLastLineRow to the follow table,
+ // or if we have to split the line:
+ bool bTableLayoutTooComplex = false;
+ tools::Long nMinHeight = 0;
+
+ // We have to take into account:
+ // 1. The fixed height of the row
+ // 2. The borders of the cells inside the row
+ // 3. The minimum height of the row
+ if ( pTmpLastLineRow->HasFixSize() )
+ nMinHeight = aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea());
+ else
+ {
+ {
+ const SwFormatFrameSize &rSz = pTmpLastLineRow->GetFormat()->GetFrameSize();
+ if ( rSz.GetHeightSizeType() == SwFrameSize::Minimum )
+ nMinHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pTmpLastLineRow);
+ }
+
+ SwFrame* pCell = pTmpLastLineRow->Lower();
+ while ( pCell )
+ {
+ if ( static_cast<SwCellFrame*>(pCell)->Lower() &&
+ static_cast<SwCellFrame*>(pCell)->Lower()->IsRowFrame() )
+ {
+ bTableLayoutTooComplex = true;
+ break;
+ }
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCell );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ nMinHeight = std::max( nMinHeight, tools::Long(lcl_CalcTopAndBottomMargin( *static_cast<SwLayoutFrame*>(pCell), rAttrs )) );
+ pCell = pCell->GetNext();
+ }
+ }
+
+ // 1. Case:
+ // The line completely fits into the master table.
+ // Nevertheless, we build a follow (otherwise painting problems
+ // with empty cell).
+
+ // 2. Case:
+ // The line has to be split, the minimum height still fits into
+ // the master table, and the table structure is not too complex.
+ if ( nTmpCut > nCurrentHeight ||
+ ( pTmpLastLineRow->IsRowSplitAllowed() &&
+ !bTableLayoutTooComplex && nMinHeight < nTmpCut ) )
+ {
+ // The line has to be split:
+ SwRowFrame* pNewRow = new SwRowFrame( *pTmpLastLineRow->GetTabLine(), &rTab, false );
+ pNewRow->SetFollowFlowRow( true );
+ pNewRow->SetFollowRow( pTmpLastLineRow->GetFollowRow() );
+ pTmpLastLineRow->SetFollowRow( pNewRow );
+ pNewRow->InsertBehind( pCurrFollowFlowLineCell, nullptr );
+ pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
+ }
+
+ // The following lines have to be moved:
+ while ( pTmpLastLineRow )
+ {
+ SwRowFrame* pTmp = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
+ lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pTmpLastLineRow );
+ pTmpLastLineRow->RemoveFromLayout();
+ pTmpLastLineRow->InsertBefore( pCurrFollowFlowLineCell, nullptr );
+ pTmpLastLineRow->Shrink( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
+ pCurrFollowFlowLineCell->Grow( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
+ pTmpLastLineRow = pTmp;
+ }
+ }
+
+ pCurrLastLineCell = static_cast<SwCellFrame*>(pCurrLastLineCell->GetNext());
+ pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(pCurrFollowFlowLineCell->GetNext());
+ }
+}
+
+// Local helper function to handle nested table cells after the split process
+static void lcl_PostprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine )
+{
+ SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
+ while ( pCurrMasterCell )
+ {
+ if ( pCurrMasterCell->Lower() &&
+ pCurrMasterCell->Lower()->IsRowFrame() )
+ {
+ SwRowFrame* pRowFrame = static_cast<SwRowFrame*>(pCurrMasterCell->GetLastLower());
+
+ if ( nullptr != pRowFrame->GetPrev() && !pRowFrame->ContainsContent() )
+ {
+ OSL_ENSURE( pRowFrame->GetFollowRow(), "Deleting row frame without follow" );
+
+ // The footnotes have to be moved:
+ lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pRowFrame );
+ pRowFrame->Cut();
+ SwRowFrame* pFollowRow = pRowFrame->GetFollowRow();
+ pRowFrame->Paste( pFollowRow->GetUpper(), pFollowRow );
+ pRowFrame->SetFollowRow( pFollowRow->GetFollowRow() );
+ lcl_MoveRowContent( *pFollowRow, *pRowFrame );
+ pFollowRow->Cut();
+ SwFrame::DestroyFrame(pFollowRow);
+ ::SwInvalidateAll( pCurrMasterCell, LONG_MAX );
+ }
+ }
+
+ pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
+ }
+}
+
+// Local helper function to re-calculate the split line.
+inline void TableSplitRecalcLock( SwFlowFrame *pTab ) { pTab->LockJoin(); }
+inline void TableSplitRecalcUnlock( SwFlowFrame *pTab ) { pTab->UnlockJoin(); }
+
+static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine,
+ SwTwips nRemainingSpaceForLastRow, SwTwips nAlreadyFree )
+{
+ bool bRet = true;
+
+ vcl::RenderContext* pRenderContext = rLastLine.getRootFrame()->GetCurrShell()->GetOut();
+ SwTabFrame& rTab = static_cast<SwTabFrame&>(*rLastLine.GetUpper());
+ SwRectFnSet aRectFnSet(rTab.GetUpper());
+ SwTwips nCurLastLineHeight = aRectFnSet.GetHeight(rLastLine.getFrameArea());
+
+ // If there are nested cells in rLastLine, the recalculation of the last
+ // line needs some preprocessing.
+ lcl_PreprocessRowsInCells( rTab, rLastLine, rFollowLine, nRemainingSpaceForLastRow );
+
+ // Here the recalculation process starts:
+ rTab.SetRebuildLastLine( true );
+ // #i26945#
+ rTab.SetDoesObjsFit( true );
+
+ // #i26945# - invalidate and move floating screen
+ // objects 'out of range'
+ ::lcl_InvalidateLowerObjs( rLastLine, true );
+
+ // manipulate row and cell sizes
+
+ // #i26945# - Do *not* consider floating screen objects
+ // for the minimal cell height.
+ rTab.SetConsiderObjsForMinCellHeight( false );
+ ::lcl_ShrinkCellsAndAllContent( rLastLine );
+ rTab.SetConsiderObjsForMinCellHeight( true );
+
+ // invalidate last line
+ ::SwInvalidateAll( &rLastLine, LONG_MAX );
+
+ // Shrink the table to account for the shrunk last row, as well as lower rows
+ // that had been moved to follow table in SwTabFrame::Split.
+ // It will grow later when last line will recalc its height.
+ rTab.Shrink(nAlreadyFree + nCurLastLineHeight - nRemainingSpaceForLastRow + 1);
+
+ // Lock this tab frame and its follow
+ bool bUnlockMaster = false;
+ SwFlowFrame * pFollow = nullptr;
+ SwTabFrame* pMaster = rTab.IsFollow() ? rTab.FindMaster() : nullptr;
+ if ( pMaster && !pMaster->IsJoinLocked() )
+ {
+ bUnlockMaster = true;
+ ::TableSplitRecalcLock( pMaster );
+ }
+ if ( !rTab.GetFollow()->IsJoinLocked() )
+ {
+ pFollow = rTab.GetFollow();
+ ::TableSplitRecalcLock( pFollow );
+ }
+
+ bool bInSplit = rLastLine.IsInSplit();
+ rLastLine.SetInSplit();
+
+ // Do the recalculation
+ lcl_RecalcRow( rLastLine, LONG_MAX );
+ // #115759# - force a format of the last line in order to
+ // get the correct height.
+ rLastLine.InvalidateSize();
+ rLastLine.Calc(pRenderContext);
+
+ rLastLine.SetInSplit(bInSplit);
+
+ // Unlock this tab frame and its follow
+ if ( pFollow )
+ ::TableSplitRecalcUnlock( pFollow );
+ if ( bUnlockMaster )
+ ::TableSplitRecalcUnlock( pMaster );
+
+ // If there are nested cells in rLastLine, the recalculation of the last
+ // line needs some postprocessing.
+ lcl_PostprocessRowsInCells( rTab, rLastLine );
+
+ // Do a couple of checks on the current situation.
+
+ // If we are not happy with the current situation we return false.
+ // This will start a new try to split the table, this time we do not
+ // try to split the table rows.
+
+ // 1. Check if table fits to its upper.
+ // #i26945# - include check, if objects fit
+ const SwTwips nDistanceToUpperPrtBottom =
+ aRectFnSet.BottomDist(rTab.getFrameArea(), aRectFnSet.GetPrtBottom(*rTab.GetUpper()));
+ // tdf#125685 ignore footnotes that are anchored in follow-table of this
+ // table - if split is successful they move to the next page/column anyway
+ assert(rTab.GetFollow() == rFollowLine.GetUpper());
+ SwTwips nFollowFootnotes(0);
+ // actually there should always be a boss frame, except if "this" isn't
+ // connected to a page yet; not sure if that can happen
+ if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
+ {
+ if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
+ {
+ for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
+ pFootnote != nullptr;
+ pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
+ {
+ SwContentFrame const*const pAnchor = pFootnote->GetRef();
+ SwTabFrame const* pTab = pAnchor->FindTabFrame();
+ if (pTab)
+ {
+ while (pTab->GetUpper()->IsInTab())
+ {
+ pTab = pTab->GetUpper()->FindTabFrame();
+ }
+ // TODO currently do this only for top-level tables?
+ // otherwise would need to check rTab's follow and any upper table's follow?
+ if (pTab == rTab.GetFollow())
+ {
+ nFollowFootnotes += aRectFnSet.GetHeight(pFootnote->getFrameArea());
+ }
+ }
+ }
+ }
+ }
+ if (nDistanceToUpperPrtBottom + nFollowFootnotes < 0 || !rTab.DoesObjsFit())
+ bRet = false;
+
+ // 2. Check if each cell in the last line has at least one content frame.
+
+ // Note: a FollowFlowRow may contains empty cells!
+ if ( bRet )
+ {
+ if ( !rLastLine.IsInFollowFlowRow() )
+ {
+ SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
+ while ( pCurrMasterCell )
+ {
+ if ( !pCurrMasterCell->ContainsContent() && pCurrMasterCell->GetTabBox()->getRowSpan() >= 1 )
+ {
+ bRet = false;
+ break;
+ }
+ pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
+ }
+ }
+ }
+
+ // 3. Check if last line does not contain any content:
+ if ( bRet )
+ {
+ if ( !rLastLine.ContainsContent() )
+ {
+ bRet = false;
+ }
+ }
+
+ // 4. Check if follow flow line does not contain content:
+ if ( bRet )
+ {
+ if ( !rFollowLine.IsRowSpanLine() && !rFollowLine.ContainsContent() )
+ {
+ bRet = false;
+ }
+ }
+
+ if ( bRet )
+ {
+ // Everything looks fine. Splitting seems to be successful. We invalidate
+ // rFollowLine to force a new formatting.
+ ::SwInvalidateAll( &rFollowLine, LONG_MAX );
+ }
+ else
+ {
+ // Splitting the table row gave us an unexpected result.
+ // Everything has to be prepared for a second try to split
+ // the table, this time without splitting the row.
+ ::SwInvalidateAll( &rLastLine, LONG_MAX );
+ }
+
+ rTab.SetRebuildLastLine( false );
+ // #i26945#
+ rTab.SetDoesObjsFit( true );
+
+ return bRet;
+}
+
+// Sets the correct height for all spanned cells
+static void lcl_AdjustRowSpanCells( SwRowFrame* pRow )
+{
+ SwRectFnSet aRectFnSet(pRow);
+ SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pRow->GetLower());
+ while ( pCellFrame )
+ {
+ const tools::Long nLayoutRowSpan = pCellFrame->GetLayoutRowSpan();
+ if ( nLayoutRowSpan > 1 )
+ {
+ // calculate height of cell:
+ const tools::Long nNewCellHeight = lcl_GetHeightOfRows( pRow, nLayoutRowSpan );
+ const tools::Long nDiff = nNewCellHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
+
+ if ( nDiff )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCellFrame);
+ aRectFnSet.AddBottom(aFrm, nDiff);
+ }
+ }
+
+ pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
+ }
+}
+
+// Returns the maximum layout row span of the row
+// Looking for the next row that contains no covered cells:
+static tools::Long lcl_GetMaximumLayoutRowSpan( const SwRowFrame& rRow )
+{
+ tools::Long nRet = 1;
+
+ const SwRowFrame* pCurrentRowFrame = static_cast<const SwRowFrame*>(rRow.GetNext());
+ bool bNextRow = false;
+
+ while ( pCurrentRowFrame )
+ {
+ // if there is any covered cell, we proceed to the next row frame
+ const SwCellFrame* pLower = static_cast<const SwCellFrame*>( pCurrentRowFrame->Lower());
+ while ( pLower )
+ {
+ if ( pLower->GetTabBox()->getRowSpan() < 0 )
+ {
+ ++nRet;
+ bNextRow = true;
+ break;
+ }
+ pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
+ }
+ pCurrentRowFrame = bNextRow ?
+ static_cast<const SwRowFrame*>(pCurrentRowFrame->GetNext() ) :
+ nullptr;
+ }
+
+ return nRet;
+}
+
+// Function to remove the FollowFlowLine of rTab.
+// The content of the FollowFlowLine is moved to the associated line in the
+// master table.
+bool SwTabFrame::RemoveFollowFlowLine()
+{
+ // find FollowFlowLine
+ SwTabFrame *pFoll = GetFollow();
+ SwRowFrame* pFollowFlowLine = pFoll ? pFoll->GetFirstNonHeadlineRow() : nullptr;
+
+ // find last row in master
+ SwFrame* pLastLine = GetLastLower();
+
+ OSL_ENSURE( HasFollowFlowLine() &&
+ pFollowFlowLine &&
+ pLastLine, "There should be a flowline in the follow" );
+
+ // #140081# Make code robust.
+ if ( !pFollowFlowLine || !pLastLine )
+ return true;
+ if (pFollowFlowLine->IsDeleteForbidden())
+ {
+ SAL_WARN("sw.layout", "Cannot remove in-use Follow Flow Line");
+ return false;
+ }
+
+ // We have to reset the flag here, because lcl_MoveRowContent
+ // calls a GrowFrame(), which has a different behavior if
+ // this flag is set.
+ SetFollowFlowLine( false );
+
+ // Move content
+ lcl_MoveRowContent( *pFollowFlowLine, *static_cast<SwRowFrame*>(pLastLine) );
+
+ // NEW TABLES
+ // If a row span follow flow line is removed, we want to move the whole span
+ // to the master:
+ tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pFollowFlowLine );
+
+ if ( nRowsToMove > 1 )
+ {
+ SwRectFnSet aRectFnSet(this);
+ SwFrame* pRow = pFollowFlowLine->GetNext();
+ SwFrame* pInsertBehind = GetLastLower();
+ SwTwips nGrow = 0;
+
+ while ( pRow && nRowsToMove-- > 1 )
+ {
+ SwFrame* pNxt = pRow->GetNext();
+ nGrow += aRectFnSet.GetHeight(pRow->getFrameArea());
+
+ // The footnotes have to be moved:
+ lcl_MoveFootnotes( *GetFollow(), *this, static_cast<SwRowFrame&>(*pRow) );
+
+ pRow->RemoveFromLayout();
+ pRow->InsertBehind( this, pInsertBehind );
+ pRow->InvalidateAll_();
+ pRow->CheckDirChange();
+ pInsertBehind = pRow;
+ pRow = pNxt;
+ }
+
+ SwFrame* pFirstRow = Lower();
+ while ( pFirstRow )
+ {
+ lcl_AdjustRowSpanCells( static_cast<SwRowFrame*>(pFirstRow) );
+ pFirstRow = pFirstRow->GetNext();
+ }
+
+ Grow( nGrow );
+ GetFollow()->Shrink( nGrow );
+ }
+
+ bool bJoin = !pFollowFlowLine->GetNext();
+ pFollowFlowLine->Cut();
+ SwFrame::DestroyFrame(pFollowFlowLine);
+
+ return bJoin;
+}
+
+// #i26945# - Floating screen objects are no longer searched.
+static bool lcl_FindSectionsInRow( const SwRowFrame& rRow )
+{
+ bool bRet = false;
+ const SwCellFrame* pLower = static_cast<const SwCellFrame*>(rRow.Lower());
+ while ( pLower )
+ {
+ if ( pLower->IsVertical() != rRow.IsVertical() )
+ return true;
+
+ const SwFrame* pTmpFrame = pLower->Lower();
+ while ( pTmpFrame )
+ {
+ if ( pTmpFrame->IsRowFrame() )
+ {
+ bRet = lcl_FindSectionsInRow( *static_cast<const SwRowFrame*>(pTmpFrame) );
+ }
+ else
+ {
+ // #i26945# - search only for sections
+ if (pTmpFrame->IsSctFrame())
+ {
+ bRet = true;
+
+ if (!rRow.IsInSct())
+ {
+ // This row is not in a section.
+ if (const SwFrame* pSectionLower = pTmpFrame->GetLower())
+ {
+ if (!pSectionLower->IsColumnFrame())
+ {
+ // Section has a single column only, try to
+ // split that.
+ bRet = false;
+
+ for (const SwFrame* pFrame = pSectionLower; pFrame; pFrame = pFrame->GetNext())
+ {
+ if (pFrame->IsTabFrame())
+ {
+ // Section contains a table, no split in that case.
+ bRet = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( bRet )
+ return true;
+ pTmpFrame = pTmpFrame->GetNext();
+ }
+
+ pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
+ }
+ return bRet;
+}
+
+bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowKeep )
+{
+ bool bRet = true;
+
+ SwRectFnSet aRectFnSet(this);
+
+ // #i26745# - format row and cell frames of table
+ {
+ Lower()->InvalidatePos_();
+ // #i43913# - correction
+ // call method <lcl_InnerCalcLayout> with first lower.
+ lcl_InnerCalcLayout( Lower(), LONG_MAX, true );
+ }
+
+ //In order to be able to compare the positions of the cells with CutPos,
+ //they have to be calculated consecutively starting from the table.
+ //They can definitely be invalid because of position changes of the table.
+ SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
+ if( !pRow )
+ return bRet;
+
+ const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
+ sal_uInt16 nRowCount = 0; // pRow currently points to the first row
+
+ SwTwips nRemainingSpaceForLastRow =
+ aRectFnSet.YDiff(nCutPos, aRectFnSet.GetTop(getFrameArea()));
+ nRemainingSpaceForLastRow -= aRectFnSet.GetTopMargin(*this);
+
+ // Make pRow point to the line that does not fit anymore:
+ while( pRow->GetNext() &&
+ nRemainingSpaceForLastRow >= ( aRectFnSet.GetHeight(pRow->getFrameArea()) +
+ (IsCollapsingBorders() ?
+ pRow->GetBottomLineSize() :
+ 0 ) ) )
+ {
+ if( bTryToSplit || !pRow->IsRowSpanLine() ||
+ 0 != aRectFnSet.GetHeight(pRow->getFrameArea()) )
+ ++nRowCount;
+ nRemainingSpaceForLastRow -= aRectFnSet.GetHeight(pRow->getFrameArea());
+ pRow = static_cast<SwRowFrame*>(pRow->GetNext());
+ }
+
+ // bSplitRowAllowed: Row may be split according to its attributes.
+ // bTryToSplit: Row will never be split if bTryToSplit = false.
+ // This can either be passed as a parameter, indicating
+ // that we are currently doing the second try to split the
+ // table, or it will be set to false under certain
+ // conditions that are not suitable for splitting
+ // the row.
+ bool bSplitRowAllowed = true;
+ if (!pRow->IsRowSplitAllowed())
+ {
+ // A row larger than the entire page ought to be allowed to split regardless of setting,
+ // otherwise it has hidden content and that makes no sense
+ if ( pRow->getFrameArea().Height() > FindPageFrame()->getFramePrintArea().Height() )
+ pRow->SetForceRowSplitAllowed( true );
+ else
+ bSplitRowAllowed = false;
+ }
+ // #i29438#
+ // #i26945# - Floating screen objects no longer forbid
+ // a splitting of the table row.
+ // Special DoNotSplit case 1:
+ // Search for sections inside pRow:
+ if ( lcl_FindSectionsInRow( *pRow ) )
+ {
+ bTryToSplit = false;
+ }
+
+ // #i29771#
+ // To avoid loops, we do some checks before actually trying to split
+ // the row. Maybe we should keep the next row in this table.
+ // Note: This is only done if we are at the beginning of our upper
+ bool bKeepNextRow = false;
+ if ( nRowCount < nRepeat )
+ {
+ // First case: One of the repeated headline does not fit to the page anymore.
+ // tdf#88496 Disable repeated headline (like for #i44910#) to avoid loops and
+ // to fix interoperability problems (very long tables only with headline)
+ // tdf#150149 except in multi-column sections, where it's possible to enlarge
+ // the height of the section frame instead of using this fallback
+ OSL_ENSURE( !GetIndPrev(), "Table is supposed to be at beginning" );
+ if ( !IsInSct() )
+ {
+ m_pTable->SetRowsToRepeat(0);
+ return false;
+ }
+ else
+ bKeepNextRow = true;
+ }
+ else if ( !GetIndPrev() && nRepeat == nRowCount )
+ {
+ // Second case: The first non-headline row does not fit to the page.
+ // If it is not allowed to be split, or it contains a sub-row that
+ // is not allowed to be split, we keep the row in this table:
+ if ( bTryToSplit && bSplitRowAllowed )
+ {
+ // Check if there are (first) rows inside this row,
+ // which are not allowed to be split.
+ SwCellFrame* pLowerCell = static_cast<SwCellFrame*>(pRow->Lower());
+ while ( pLowerCell )
+ {
+ if ( pLowerCell->Lower() && pLowerCell->Lower()->IsRowFrame() )
+ {
+ const SwRowFrame* pLowerRow = static_cast<SwRowFrame*>(pLowerCell->Lower());
+ if ( !pLowerRow->IsRowSplitAllowed() &&
+ aRectFnSet.GetHeight(pLowerRow->getFrameArea()) > nRemainingSpaceForLastRow )
+ {
+ bKeepNextRow = true;
+ break;
+ }
+ }
+ pLowerCell = static_cast<SwCellFrame*>(pLowerCell->GetNext());
+ }
+ }
+ else
+ bKeepNextRow = true;
+ }
+
+ // Better keep the next row in this table:
+ if ( bKeepNextRow )
+ {
+ pRow = GetFirstNonHeadlineRow();
+ if ( pRow && pRow->IsRowSpanLine() && 0 == aRectFnSet.GetHeight(pRow->getFrameArea()) )
+ pRow = static_cast<SwRowFrame*>(pRow->GetNext());
+ if ( pRow )
+ {
+ pRow = static_cast<SwRowFrame*>(pRow->GetNext());
+ ++nRowCount;
+ }
+ }
+
+ // No more row to split or to move to follow table:
+ if ( !pRow )
+ return bRet;
+
+ // We try to split the row if
+ // - the attributes of the row are set accordingly and
+ // - we are allowed to do so
+ // - it should not be kept with the next row
+ bSplitRowAllowed = bSplitRowAllowed && bTryToSplit &&
+ ( !bTableRowKeep ||
+ !pRow->ShouldRowKeepWithNext() );
+
+ // Adjust pRow according to the keep-with-next attribute:
+ if ( !bSplitRowAllowed && bTableRowKeep )
+ {
+ SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pRow->GetPrev());
+ SwRowFrame* pOldRow = pRow;
+ while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() &&
+ nRowCount > nRepeat )
+ {
+ pRow = pTmpRow;
+ --nRowCount;
+ pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetPrev());
+ }
+
+ // loop prevention
+ if ( nRowCount == nRepeat && !GetIndPrev())
+ {
+ pRow = pOldRow;
+ }
+ }
+
+ // If we do not intend to split pRow, we check if we are
+ // allowed to move pRow to a follow. Otherwise we return
+ // false, indicating an error
+ if ( !bSplitRowAllowed )
+ {
+ SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
+ if ( pRow == pFirstNonHeadlineRow )
+ return false;
+
+ // #i91764#
+ // Ignore row span lines
+ SwRowFrame* pTmpRow = pFirstNonHeadlineRow;
+ while ( pTmpRow && pTmpRow->IsRowSpanLine() )
+ {
+ pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
+ }
+ if ( !pTmpRow || pRow == pTmpRow )
+ {
+ return false;
+ }
+ }
+
+ // Build follow table if not already done:
+ bool bNewFollow;
+ SwTabFrame *pFoll;
+ if ( GetFollow() )
+ {
+ pFoll = GetFollow();
+ bNewFollow = false;
+ }
+ else
+ {
+ bNewFollow = true;
+ pFoll = new SwTabFrame( *this );
+
+ // We give the follow table an initial width.
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFoll);
+ aRectFnSet.AddWidth(aFrm, aRectFnSet.GetWidth(getFrameArea()));
+ aRectFnSet.SetLeft(aFrm, aRectFnSet.GetLeft(getFrameArea()));
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFoll);
+ aRectFnSet.AddWidth(aPrt, aRectFnSet.GetWidth(getFramePrintArea()));
+ }
+
+ // Insert the new follow table
+ pFoll->InsertBehind( GetUpper(), this );
+
+ // Repeat the headlines.
+ auto& rLines = GetTable()->GetTabLines();
+ for ( nRowCount = 0; nRowCount < nRepeat; ++nRowCount )
+ {
+ // Insert new headlines:
+ SwRowFrame* pHeadline = new SwRowFrame(*rLines[nRowCount], this);
+ {
+ sw::FlyCreationSuppressor aSuppressor;
+ pHeadline->SetRepeatedHeadline(true);
+ }
+ pHeadline->InsertBefore( pFoll, nullptr );
+
+ SwPageFrame *pPage = pHeadline->FindPageFrame();
+ const SwFrameFormats *pTable = GetFormat()->GetDoc()->GetSpzFrameFormats();
+ if( !pTable->empty() )
+ {
+ SwNodeOffset nIndex;
+ SwContentFrame* pFrame = pHeadline->ContainsContent();
+ while( pFrame )
+ {
+ // sw_redlinehide: the implementation of AppendObjs
+ // takes care of iterating merged SwTextFrame
+ nIndex = pFrame->IsTextFrame()
+ ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->GetIndex()
+ : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->GetIndex();
+ AppendObjs( pTable, nIndex, pFrame, pPage, GetFormat()->GetDoc());
+ pFrame = pFrame->GetNextContentFrame();
+ if( !pHeadline->IsAnLower( pFrame ) )
+ break;
+ }
+ }
+ }
+ }
+
+ SwRowFrame* pLastRow = nullptr; // points to the last remaining line in master
+ SwRowFrame* pFollowRow = nullptr; // points to either the follow flow line or the
+ // first regular line in the follow
+
+ if ( bSplitRowAllowed )
+ {
+ // If the row that does not fit anymore is allowed
+ // to be split, the next row has to be moved to the follow table.
+ pLastRow = pRow;
+ pRow = static_cast<SwRowFrame*>(pRow->GetNext());
+
+ // new follow flow line for last row of master table
+ pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, false );
+ }
+ else
+ {
+ pFollowRow = pRow;
+
+ // NEW TABLES
+ // check if we will break a row span by moving pFollowRow to the follow:
+ // In this case we want to reformat the last line.
+ const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(pFollowRow->GetLower());
+ while ( pCellFrame )
+ {
+ if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
+ {
+ pLastRow = static_cast<SwRowFrame*>(pRow->GetPrev());
+ break;
+ }
+
+ pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext());
+ }
+
+ // new follow flow line for last row of master table
+ if ( pLastRow )
+ pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, true );
+ }
+
+ SwTwips nShrink = 0;
+
+ //Optimization: There is no paste needed for the new Follow and the
+ //optimized insert can be used (large numbers of rows luckily only occur in
+ //such situations).
+ if ( bNewFollow )
+ {
+ SwFrame* pInsertBehind = pFoll->GetLastLower();
+
+ while ( pRow )
+ {
+ SwFrame* pNxt = pRow->GetNext();
+ nShrink += aRectFnSet.GetHeight(pRow->getFrameArea());
+ // The footnotes do not have to be moved, this is done in the
+ // MoveFwd of the follow table!!!
+ pRow->RemoveFromLayout();
+ pRow->InsertBehind( pFoll, pInsertBehind );
+ pRow->InvalidateAll_();
+ pInsertBehind = pRow;
+ pRow = static_cast<SwRowFrame*>(pNxt);
+ }
+ }
+ else
+ {
+ SwFrame* pPasteBefore = HasFollowFlowLine() ?
+ pFollowRow->GetNext() :
+ pFoll->GetFirstNonHeadlineRow();
+
+ while ( pRow )
+ {
+ SwFrame* pNxt = pRow->GetNext();
+ nShrink += aRectFnSet.GetHeight(pRow->getFrameArea());
+
+ // The footnotes have to be moved:
+ lcl_MoveFootnotes( *this, *GetFollow(), *pRow );
+
+ pRow->RemoveFromLayout();
+ pRow->Paste( pFoll, pPasteBefore );
+
+ pRow->CheckDirChange();
+ pRow = static_cast<SwRowFrame*>(pNxt);
+ }
+ }
+
+ if ( !pLastRow )
+ Shrink( nShrink );
+ else
+ {
+ // we rebuild the last line to assure that it will be fully formatted
+ // we also don't shrink here, because we will be doing that in lcl_RecalcSplitLine
+
+ // recalculate the split line
+ bRet = lcl_RecalcSplitLine( *pLastRow, *pFollowRow, nRemainingSpaceForLastRow, nShrink );
+
+ // RecalcSplitLine did not work. In this case we conceal the split error:
+ if (!bRet && !bSplitRowAllowed)
+ {
+ bRet = true;
+ }
+
+ // NEW TABLES
+ // check if each cell in the row span line has a good height
+ if ( bRet && pFollowRow->IsRowSpanLine() )
+ lcl_AdjustRowSpanCells( pFollowRow );
+ }
+
+ return bRet;
+}
+
+namespace
+{
+ bool CanDeleteFollow(SwTabFrame *pFoll)
+ {
+ if (pFoll->IsJoinLocked())
+ return false;
+
+ if (pFoll->IsDeleteForbidden())
+ {
+ SAL_WARN("sw.layout", "Delete Forbidden");
+ return false;
+ }
+
+ return true;
+ }
+}
+
+void SwTabFrame::Join()
+{
+ OSL_ENSURE( !HasFollowFlowLine(), "Joining follow flow line" );
+
+ SwTabFrame *pFoll = GetFollow();
+
+ if (!pFoll || !CanDeleteFollow(pFoll))
+ return;
+
+ SwRectFnSet aRectFnSet(this);
+ pFoll->Cut(); //Cut out first to avoid unnecessary notifications.
+
+ SwFrame *pRow = pFoll->GetFirstNonHeadlineRow(),
+ *pNxt;
+
+ SwFrame* pPrv = GetLastLower();
+
+ SwTwips nHeight = 0; //Total height of the inserted rows as return value.
+
+ while ( pRow )
+ {
+ pNxt = pRow->GetNext();
+ nHeight += aRectFnSet.GetHeight(pRow->getFrameArea());
+ pRow->RemoveFromLayout();
+ pRow->InvalidateAll_();
+ pRow->InsertBehind( this, pPrv );
+ pRow->CheckDirChange();
+ pPrv = pRow;
+ pRow = pNxt;
+ }
+
+ SetFollow( pFoll->GetFollow() );
+ SetFollowFlowLine( pFoll->HasFollowFlowLine() );
+ SwFrame::DestroyFrame(pFoll);
+
+ Grow( nHeight );
+}
+
+static void SwInvalidatePositions( SwFrame *pFrame, tools::Long nBottom )
+{
+ // LONG_MAX == nBottom means we have to calculate all
+ bool bAll = LONG_MAX == nBottom;
+ SwRectFnSet aRectFnSet(pFrame);
+ do
+ { pFrame->InvalidatePos_();
+ pFrame->InvalidateSize_();
+ if( pFrame->IsLayoutFrame() )
+ {
+ if ( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
+ {
+ ::SwInvalidatePositions( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
+ // #i26945#
+ ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pFrame) );
+ }
+ }
+ else
+ pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ pFrame = pFrame->GetNext();
+ } while ( pFrame &&
+ ( bAll ||
+ aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
+}
+
+void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom )
+{
+ // LONG_MAX == nBottom means we have to calculate all
+ bool bAll = LONG_MAX == nBottom;
+ SwRectFnSet aRectFnSet(pFrame);
+ do
+ {
+ pFrame->InvalidatePos_();
+ pFrame->InvalidateSize_();
+ pFrame->InvalidatePrt_();
+ if( pFrame->IsLayoutFrame() )
+ {
+ // NEW TABLES
+ SwLayoutFrame* pToInvalidate = static_cast<SwLayoutFrame*>(pFrame);
+ if (pFrame->IsCellFrame())
+ {
+ SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
+ if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
+ {
+ pToInvalidate = & const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
+ pToInvalidate->InvalidatePos_();
+ pToInvalidate->InvalidateSize_();
+ pToInvalidate->InvalidatePrt_();
+ }
+ }
+ if ( pToInvalidate->Lower() )
+ ::SwInvalidateAll( pToInvalidate->Lower(), nBottom);
+ }
+ else
+ pFrame->Prepare();
+
+ pFrame = pFrame->GetNext();
+ } while ( pFrame &&
+ ( bAll ||
+ aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
+}
+
+// #i29550#
+static void lcl_InvalidateAllLowersPrt( SwLayoutFrame* pLayFrame )
+{
+ pLayFrame->InvalidatePrt_();
+ pLayFrame->InvalidateSize_();
+ pLayFrame->SetCompletePaint();
+
+ SwFrame* pFrame = pLayFrame->Lower();
+
+ while ( pFrame )
+ {
+ if ( pFrame->IsLayoutFrame() )
+ lcl_InvalidateAllLowersPrt( static_cast<SwLayoutFrame*>(pFrame) );
+ else
+ {
+ pFrame->InvalidatePrt_();
+ pFrame->InvalidateSize_();
+ pFrame->SetCompletePaint();
+ }
+
+ pFrame = pFrame->GetNext();
+ }
+}
+
+bool SwContentFrame::CalcLowers(SwLayoutFrame & rLay, SwLayoutFrame const& rDontLeave,
+ tools::Long nBottom, bool bSkipRowSpanCells )
+{
+ vcl::RenderContext* pRenderContext = rLay.getRootFrame()->GetCurrShell()->GetOut();
+ // LONG_MAX == nBottom means we have to calculate all
+ bool bAll = LONG_MAX == nBottom;
+ bool bRet = false;
+ SwContentFrame *pCnt = rLay.ContainsContent();
+ SwRectFnSet aRectFnSet(&rLay);
+
+ // FME 2007-08-30 #i81146# new loop control
+ int nLoopControlRuns = 0;
+ const int nLoopControlMax = 10;
+ const sw::BroadcastingModify* pLoopControlCond = nullptr;
+
+ while (pCnt && rDontLeave.IsAnLower(pCnt))
+ {
+ // #115759# - check, if a format of content frame is
+ // possible. Thus, 'copy' conditions, found at the beginning of
+ // <SwContentFrame::MakeAll(..)>, and check these.
+ const bool bFormatPossible = !pCnt->IsJoinLocked() &&
+ ( !pCnt->IsTextFrame() ||
+ !static_cast<SwTextFrame*>(pCnt)->IsLocked() ) &&
+ ( pCnt->IsFollow() || !StackHack::IsLocked() );
+
+ // NEW TABLES
+ bool bSkipContent = false;
+ if ( bSkipRowSpanCells && pCnt->IsInTab() )
+ {
+ const SwFrame* pCell = pCnt->GetUpper();
+ while ( pCell && !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+ if ( pCell && 1 != static_cast<const SwCellFrame*>( pCell )->GetLayoutRowSpan() )
+ bSkipContent = true;
+ }
+
+ if ( bFormatPossible && !bSkipContent )
+ {
+ bRet |= !pCnt->isFrameAreaDefinitionValid();
+ // #i26945# - no extra invalidation of floating
+ // screen objects needed.
+ // Thus, delete call of method <SwFrame::InvalidateObjs( true )>
+ pCnt->Calc(pRenderContext);
+ // #i46941# - frame has to be valid
+ // Note: frame could be invalid after calling its format, if it's locked.
+ OSL_ENSURE( !pCnt->IsTextFrame() ||
+ pCnt->isFrameAreaDefinitionValid() ||
+ static_cast<SwTextFrame*>(pCnt)->IsJoinLocked(),
+ "<SwContentFrame::CalcLowers(..)> - text frame invalid and not locked." );
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ // #i23129#, #i36347# - pass correct page frame to
+ // the object formatter
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ SwTextNode const*const pTextNode(
+ static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst());
+ if (pTextNode == pLoopControlCond)
+ ++nLoopControlRuns;
+ else
+ {
+ nLoopControlRuns = 0;
+ pLoopControlCond = pTextNode;
+ }
+
+ if ( nLoopControlRuns < nLoopControlMax )
+ {
+ // restart format with first content
+ pCnt = rLay.ContainsContent();
+ continue;
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ OSL_FAIL( "LoopControl in SwContentFrame::CalcLowers" );
+#endif
+ }
+ }
+ if (!rDontLeave.IsAnLower(pCnt)) // moved backward?
+ {
+ pCnt = rLay.ContainsContent();
+ continue; // avoid formatting new upper on different page
+ }
+ pCnt->GetUpper()->Calc(pRenderContext);
+ }
+ if( ! bAll && aRectFnSet.YDiff(aRectFnSet.GetTop(pCnt->getFrameArea()), nBottom) > 0 )
+ break;
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ return bRet;
+}
+
+// #i26945# - add parameter <_bOnlyRowsAndCells> to control
+// that only row and cell frames are formatted.
+static bool lcl_InnerCalcLayout( SwFrame *pFrame,
+ tools::Long nBottom,
+ bool _bOnlyRowsAndCells )
+{
+ vcl::RenderContext* pRenderContext = pFrame->getRootFrame()->GetCurrShell() ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
+ // LONG_MAX == nBottom means we have to calculate all
+ bool bAll = LONG_MAX == nBottom;
+ bool bRet = false;
+ const SwFrame* pOldUp = pFrame->GetUpper();
+ SwRectFnSet aRectFnSet(pFrame);
+ do
+ {
+ // #i26945# - parameter <_bOnlyRowsAndCells> controls,
+ // if only row and cell frames are formatted.
+ if ( pFrame->IsLayoutFrame() &&
+ ( !_bOnlyRowsAndCells || pFrame->IsRowFrame() || pFrame->IsCellFrame() ) )
+ {
+ SwFrameDeleteGuard aDeleteGuard(pFrame);
+
+ // #130744# An invalid locked table frame will
+ // not be calculated => It will not become valid =>
+ // Loop in lcl_RecalcRow(). Therefore we do not consider them for bRet.
+ bRet |= !pFrame->isFrameAreaDefinitionValid() && ( !pFrame->IsTabFrame() || !static_cast<SwTabFrame*>(pFrame)->IsJoinLocked() );
+ pFrame->Calc(pRenderContext);
+ if( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
+ bRet |= lcl_InnerCalcLayout( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
+
+ // NEW TABLES
+ if (pFrame->IsCellFrame())
+ {
+ SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
+ if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
+ {
+ SwCellFrame& rToCalc = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
+ bRet |= !rToCalc.isFrameAreaDefinitionValid();
+ rToCalc.Calc(pRenderContext);
+ if ( rToCalc.Lower() )
+ bRet |= lcl_InnerCalcLayout( rToCalc.Lower(), nBottom);
+ }
+ }
+ }
+ pFrame = pFrame->GetNext();
+ } while( pFrame &&
+ ( bAll ||
+ aRectFnSet.YDiff(aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom) < 0 )
+ && pFrame->GetUpper() == pOldUp );
+ return bRet;
+}
+
+static void lcl_RecalcRow(SwRowFrame & rRow, tools::Long const nBottom)
+{
+ // FME 2007-08-30 #i81146# new loop control
+ int nLoopControlRuns_1 = 0;
+ sal_uInt16 nLoopControlStage_1 = 0;
+ const int nLoopControlMax = 10;
+
+ bool bCheck = true;
+ do
+ {
+ // FME 2007-08-30 #i81146# new loop control
+ int nLoopControlRuns_2 = 0;
+ sal_uInt16 nLoopControlStage_2 = 0;
+
+ while (lcl_InnerCalcLayout(&rRow, nBottom))
+ {
+ if ( ++nLoopControlRuns_2 > nLoopControlMax )
+ {
+ SAL_WARN_IF(nLoopControlStage_2 == 0, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 1!");
+ SAL_WARN_IF(nLoopControlStage_2 == 1, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 2!!");
+ SAL_WARN_IF(nLoopControlStage_2 >= 2, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 3!!!");
+ rRow.ValidateThisAndAllLowers( nLoopControlStage_2++ );
+ nLoopControlRuns_2 = 0;
+ if( nLoopControlStage_2 > 2 )
+ break;
+ }
+
+ bCheck = true;
+ }
+
+ if( bCheck )
+ {
+ // #115759# - force another format of the
+ // lowers, if at least one of it was invalid.
+ bCheck = SwContentFrame::CalcLowers(rRow, *rRow.GetUpper(), nBottom, true);
+
+ // NEW TABLES
+ // First we calculate the cells with row span of < 1, afterwards
+ // all cells with row span of > 1:
+ for ( int i = 0; i < 2; ++i )
+ {
+ SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(rRow.Lower());
+ while ( pCellFrame )
+ {
+ const bool bCalc = 0 == i ?
+ pCellFrame->GetLayoutRowSpan() < 1 :
+ pCellFrame->GetLayoutRowSpan() > 1;
+
+ if ( bCalc )
+ {
+ SwCellFrame& rToRecalc = 0 == i ?
+ const_cast<SwCellFrame&>(pCellFrame->FindStartEndOfRowSpanCell( true )) :
+ *pCellFrame;
+ bCheck |= SwContentFrame::CalcLowers(rToRecalc, rToRecalc, nBottom, false);
+ }
+
+ pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
+ }
+ }
+
+ if ( bCheck )
+ {
+ if ( ++nLoopControlRuns_1 > nLoopControlMax )
+ {
+ SAL_WARN_IF(nLoopControlStage_1 == 0, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 1!");
+ SAL_WARN_IF(nLoopControlStage_1 == 1, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 2!!");
+ SAL_WARN_IF(nLoopControlStage_1 >= 2, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 3!!!");
+ rRow.ValidateThisAndAllLowers( nLoopControlStage_1++ );
+ nLoopControlRuns_1 = 0;
+ if( nLoopControlStage_1 > 2 )
+ break;
+ }
+
+ continue;
+ }
+ }
+ break;
+ } while( true );
+}
+
+static void lcl_RecalcTable( SwTabFrame& rTab,
+ SwLayoutFrame *pFirstRow,
+ SwLayNotify &rNotify )
+{
+ if ( rTab.Lower() )
+ {
+ if ( !pFirstRow )
+ {
+ pFirstRow = static_cast<SwLayoutFrame*>(rTab.Lower());
+ rNotify.SetLowersComplete( true );
+ }
+ ::SwInvalidatePositions( pFirstRow, LONG_MAX );
+ lcl_RecalcRow( *static_cast<SwRowFrame*>(pFirstRow), LONG_MAX );
+ }
+}
+
+// This is a new function to check the first condition whether
+// a tab frame may move backward. It replaces the formerly used
+// GetIndPrev(), which did not work correctly for #i5947#
+static bool lcl_NoPrev( const SwFrame& rFrame )
+{
+ // #i79774#
+ // skip empty sections on investigation of direct previous frame.
+ // use information, that at least one empty section is skipped in the following code.
+ bool bSkippedDirectPrevEmptySection( false );
+ if ( rFrame.GetPrev() )
+ {
+ const SwFrame* pPrev( rFrame.GetPrev() );
+ while ( pPrev &&
+ pPrev->IsSctFrame() &&
+ !dynamic_cast<const SwSectionFrame&>(*pPrev).GetSection() )
+ {
+ pPrev = pPrev->GetPrev();
+ bSkippedDirectPrevEmptySection = true;
+ }
+ if ( pPrev )
+ {
+ return false;
+ }
+ }
+
+ if ( ( !bSkippedDirectPrevEmptySection && !rFrame.GetIndPrev() ) ||
+ ( bSkippedDirectPrevEmptySection &&
+ ( !rFrame.IsInSct() || !rFrame.GetIndPrev_() ) ) )
+ {
+ return true;
+ }
+
+ // I do not have a direct prev, but I have an indirect prev.
+ // In section frames I have to check if I'm located inside
+ // the first column:
+ if ( rFrame.IsInSct() )
+ {
+ const SwFrame* pSct = rFrame.GetUpper();
+ if ( pSct && pSct->IsColBodyFrame() &&
+ pSct->GetUpper()->GetUpper()->IsSctFrame() )
+ {
+ const SwFrame* pPrevCol = rFrame.GetUpper()->GetUpper()->GetPrev();
+ if ( pPrevCol )
+ // I'm not inside the first column and do not have a direct
+ // prev. I can try to go backward.
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#define KEEPTAB ( !GetFollow() && !IsFollow() )
+
+// - helper method to find next content frame of
+// a table frame and format it to assure keep attribute.
+// method return true, if a next content frame is formatted.
+// Precondition: The given table frame hasn't a follow and isn't a follow.
+SwFrame* sw_FormatNextContentForKeep( SwTabFrame* pTabFrame )
+{
+ vcl::RenderContext* pRenderContext = pTabFrame->getRootFrame()->GetCurrShell()->GetOut();
+ // find next content, table or section
+ SwFrame* pNxt = pTabFrame->FindNext();
+
+ // skip empty sections
+ while ( pNxt && pNxt->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pNxt)->GetSection() )
+ {
+ pNxt = pNxt->FindNext();
+ }
+
+ // if found next frame is a section, get its first content.
+ if ( pNxt && pNxt->IsSctFrame() )
+ {
+ pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
+ }
+
+ // format found next frame.
+ // if table frame is inside another table, method <SwFrame::MakeAll()> is
+ // called to avoid that the superior table frame is formatted.
+ if ( pNxt )
+ {
+ if ( pTabFrame->GetUpper()->IsInTab() )
+ pNxt->MakeAll(pNxt->getRootFrame()->GetCurrShell()->GetOut());
+ else
+ pNxt->Calc(pRenderContext);
+ }
+
+ return pNxt;
+}
+
+namespace {
+ bool AreAllRowsKeepWithNext( const SwRowFrame* pFirstRowFrame, const bool bCheckParents = true )
+ {
+ bool bRet = pFirstRowFrame != nullptr &&
+ pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
+
+ while ( bRet && pFirstRowFrame->GetNext() != nullptr )
+ {
+ pFirstRowFrame = dynamic_cast<const SwRowFrame*>(pFirstRowFrame->GetNext());
+ bRet = pFirstRowFrame != nullptr &&
+ pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
+ }
+
+ return bRet;
+ }
+}
+
+// extern because static can't be friend
+void FriendHackInvalidateRowFrame(SwFrameAreaDefinition & rRowFrame)
+{
+ // hilariously static_cast<SwTabFrame*>(GetLower()) would not require friend declaration, but it's UB...
+ rRowFrame.setFrameAreaPositionValid(false);
+}
+
+static void InvalidateFramePositions(SwFrame * pFrame)
+{
+ while (pFrame)
+ {
+ if (pFrame->IsLayoutFrame())
+ {
+ InvalidateFramePositions(pFrame->GetLower());
+ }
+ else if (pFrame->IsTextFrame())
+ {
+ pFrame->Prepare(PrepareHint::FramePositionChanged);
+ }
+ pFrame = pFrame->GetNext();
+ }
+}
+
+void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ if ( IsJoinLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
+ return;
+
+ if ( HasFollow() )
+ {
+ SwTabFrame* pFollowFrame = GetFollow();
+ OSL_ENSURE( !pFollowFrame->IsJoinLocked() || !pFollowFrame->IsRebuildLastLine(),
+ "SwTabFrame::MakeAll for master while follow is in RebuildLastLine()" );
+ if ( pFollowFrame->IsJoinLocked() && pFollowFrame->IsRebuildLastLine() )
+ return;
+ }
+
+ PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
+
+ LockJoin(); //I don't want to be destroyed on the way.
+ SwLayNotify aNotify( this ); //does the notification in the DTor
+ // If pos is invalid, we have to call a SetInvaKeep at aNotify.
+ // Otherwise the keep attribute would not work in front of a table.
+ const bool bOldValidPos = isFrameAreaPositionValid();
+
+ //If my neighbour is my Follow at the same time, I'll swallow it up.
+ // OD 09.04.2003 #108698# - join all follows, which are placed on the
+ // same page/column.
+ // OD 29.04.2003 #109213# - join follow, only if join for the follow
+ // is not locked. Otherwise, join will not be performed and this loop
+ // will be endless.
+ while ( GetNext() && GetNext() == GetFollow() &&
+ CanDeleteFollow(GetFollow())
+ )
+ {
+ if ( HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+ Join();
+ }
+
+ // The bRemoveFollowFlowLinePending is set if the split attribute of the
+ // last line is set:
+ if ( IsRemoveFollowFlowLinePending() && HasFollowFlowLine() )
+ {
+ if ( RemoveFollowFlowLine() )
+ Join();
+ SetRemoveFollowFlowLinePending( false );
+ }
+
+ if (m_bResizeHTMLTable) //Optimized interplay with grow/shrink of the content
+ {
+ m_bResizeHTMLTable = false;
+ SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
+ if ( pLayout )
+ m_bCalcLowers = pLayout->Resize(
+ pLayout->GetBrowseWidthByTabFrame( *this ) );
+ }
+
+ // as long as bMakePage is true, a new page can be created (exactly once)
+ bool bMakePage = true;
+ // bMovedBwd gets set to true when the frame flows backwards
+ bool bMovedBwd = false;
+ // as long as bMovedFwd is false, the Frame may flow backwards (until
+ // it has been moved forward once)
+ bool bMovedFwd = false;
+ // gets set to true when the Frame is split
+ bool bSplit = false;
+ const bool bFootnotesInDoc = !GetFormat()->GetDoc()->GetFootnoteIdxs().empty();
+ const bool bFly = IsInFly();
+
+ std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
+ const SwBorderAttrs *pAttrs = oAccess->Get();
+
+ // All rows should keep together
+ const bool bDontSplit = !IsFollow() &&
+ ( !GetFormat()->GetLayoutSplit().GetValue() );
+
+ // The number of repeated headlines
+ const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
+
+ // This flag indicates that we are allowed to try to split the
+ // table rows.
+ bool bTryToSplit = true;
+
+ // Indicates that two individual rows may keep together, based on the keep
+ // attribute set at the first paragraph in the first cell.
+ const bool bTableRowKeep = !bDontSplit && GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP);
+
+ // The Magic Move: Used for the table row keep feature.
+ // If only the last row of the table wants to keep (implicitly by setting
+ // keep for the first paragraph in the first cell), and this table does
+ // not have a next, the last line will be cut. Loop prevention: Only
+ // one try.
+ // WHAT IS THIS??? It "magically" hides last line (paragraph) in a table,
+ // if first is set to keep with next???
+ bool bLastRowHasToMoveToFollow = false;
+ bool bLastRowMoveNoMoreTries = false;
+
+ const bool bLargeTable = GetTable()->GetTabLines().size() > 64; //arbitrary value, virtually guaranteed to be larger than one page.
+ const bool bEmulateTableKeep = !bLargeTable && bTableRowKeep
+ && !pAttrs->GetAttrSet().GetKeep().GetValue()
+ && AreAllRowsKeepWithNext(GetFirstNonHeadlineRow(), /*bCheckParents=*/false);
+ // The beloved keep attribute
+ const bool bKeep = IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), bEmulateTableKeep);
+
+ // Join follow table, if this table is not allowed to split:
+ if ( bDontSplit )
+ {
+ while ( GetFollow() && !GetFollow()->IsJoinLocked() )
+ {
+ if ( HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+ Join();
+ }
+ }
+
+ // Join follow table, if this does not have enough (repeated) lines:
+ if ( nRepeat )
+ {
+ if( GetFollow() && !GetFollow()->IsJoinLocked() &&
+ nullptr == GetFirstNonHeadlineRow() )
+ {
+ if ( HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+ Join();
+ }
+ }
+
+ // Join follow table, if last row of this table should keep:
+ if ( bTableRowKeep && GetFollow() && !GetFollow()->IsJoinLocked() )
+ {
+ const SwRowFrame* pTmpRow = static_cast<const SwRowFrame*>(GetLastLower());
+ if ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
+ {
+ if ( HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+ Join();
+ }
+ }
+
+ // a new one is moved forwards immediately
+ if ( !getFrameArea().Top() && IsFollow() )
+ {
+ SwFrame *pPre = GetPrev();
+ if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
+ {
+ // don't make the effort to move fwd if its known
+ // conditions that are known not to work
+ if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
+ bMakePage = false;
+ else if (!MoveFwd(bMakePage, false))
+ bMakePage = false;
+ bMovedFwd = true;
+ }
+ }
+
+ int nUnSplitted = 5; // Just another loop control :-(
+ int nThrowAwayValidLayoutLimit = 5; // And another one :-(
+ SwRectFnSet aRectFnSet(this);
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ const bool bMoveable = IsMoveable();
+ if (bMoveable &&
+ !(bMovedFwd && bEmulateTableKeep) )
+ if ( CheckMoveFwd( bMakePage, bKeep && KEEPTAB, bEmulateTableKeep ) )
+ {
+ bMovedFwd = true;
+ m_bCalcLowers = true;
+ // #i99267#
+ // reset <bSplit> after forward move to assure that follows
+ // can be joined, if further space is available.
+ bSplit = false;
+ }
+
+ Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
+ MakePos();
+
+ if ( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
+ {
+ if ( aOldPos.Y() != aRectFnSet.GetTop(getFrameArea()) )
+ {
+ SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
+ if( pLayout )
+ {
+ oAccess.reset();
+ m_bCalcLowers |= pLayout->Resize(
+ pLayout->GetBrowseWidthByTabFrame( *this ) );
+ }
+
+ setFramePrintAreaValid(false);
+ aNotify.SetLowersComplete( false );
+ }
+ SwFrame *pPre;
+ if ( bKeep || (nullptr != (pPre = FindPrev()) &&
+ pPre->GetAttrSet()->GetKeep().GetValue()) )
+ {
+ m_bCalcLowers = true;
+ }
+ if (GetLower())
+ { // it's possible that the rows already have valid pos - but it is surely wrong if the table's pos changed!
+ FriendHackInvalidateRowFrame(*GetLower());
+ // invalidate text frames to get rid of their SwFlyPortions
+ InvalidateFramePositions(GetLower());
+ }
+ }
+
+ //We need to know the height of the first row, because the master needs
+ //to be invalidated if it shrinks and then absorb the row if possible.
+ tools::Long n1StLineHeight = 0;
+ if ( IsFollow() )
+ {
+ SwFrame* pFrame = GetFirstNonHeadlineRow();
+ if ( pFrame )
+ n1StLineHeight = aRectFnSet.GetHeight(pFrame->getFrameArea());
+ }
+
+ if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ const tools::Long nOldPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
+ const tools::Long nOldFrameWidth = aRectFnSet.GetWidth(getFrameArea());
+ const Point aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
+
+ if (!oAccess)
+ {
+ oAccess.emplace(SwFrame::GetCache(), this);
+ pAttrs = oAccess->Get();
+ }
+ Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
+
+ SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
+ if ( pLayout &&
+ (aRectFnSet.GetWidth(getFramePrintArea()) != nOldPrtWidth ||
+ aRectFnSet.GetWidth(getFrameArea()) != nOldFrameWidth) )
+ {
+ oAccess.reset();
+ m_bCalcLowers |= pLayout->Resize(
+ pLayout->GetBrowseWidthByTabFrame( *this ) );
+ }
+ if ( aOldPrtPos != aRectFnSet.GetPos(getFramePrintArea()) )
+ aNotify.SetLowersComplete( false );
+ }
+
+ // If this is the first one in a chain, check if this can flow
+ // backwards (if this is movable at all).
+ // To prevent oscillations/loops, check that this has not just
+ // flowed forwards.
+ if ( !bMovedFwd && (bMoveable || bFly) && lcl_NoPrev( *this ) )
+ {
+ // for Follows notify Master.
+ // only move Follow if it has to skip empty pages.
+ if ( IsFollow() )
+ {
+ // Only if the height of the first line got smaller.
+ SwFrame *pFrame = GetFirstNonHeadlineRow();
+ if( pFrame && n1StLineHeight >aRectFnSet.GetHeight(pFrame->getFrameArea()) )
+ {
+ SwTabFrame *pMaster = FindMaster();
+ bool bDummy;
+ if ( ShouldBwdMoved( pMaster->GetUpper(), bDummy ) )
+ pMaster->InvalidatePos();
+ }
+ }
+ SwFootnoteBossFrame *pOldBoss = bFootnotesInDoc ? FindFootnoteBossFrame( true ) : nullptr;
+ bool bReformat;
+ std::optional<SfxDeleteListener> oDeleteListener;
+ if (pOldBoss)
+ oDeleteListener.emplace(*pOldBoss);
+ SwFrameDeleteGuard g(this);
+ if ( MoveBwd( bReformat ) )
+ {
+ SAL_WARN_IF(oDeleteListener && oDeleteListener->WasDeleted(), "sw.layout", "SwFootnoteBossFrame unexpectedly deleted");
+
+ aRectFnSet.Refresh(this);
+ bMovedBwd = true;
+ aNotify.SetLowersComplete( false );
+ if (bFootnotesInDoc && !oDeleteListener->WasDeleted())
+ MoveLowerFootnotes( nullptr, pOldBoss, nullptr, true );
+ if ( bReformat || bKeep )
+ {
+ tools::Long nOldTop = aRectFnSet.GetTop(getFrameArea());
+ MakePos();
+ if( nOldTop != aRectFnSet.GetTop(getFrameArea()) )
+ {
+ SwHTMLTableLayout *pHTMLLayout =
+ GetTable()->GetHTMLTableLayout();
+ if( pHTMLLayout )
+ {
+ oAccess.reset();
+ m_bCalcLowers |= pHTMLLayout->Resize(
+ pHTMLLayout->GetBrowseWidthByTabFrame( *this ) );
+ }
+
+ setFramePrintAreaValid(false);
+
+ if (!oAccess)
+ {
+ oAccess.emplace(SwFrame::GetCache(), this);
+ pAttrs = oAccess->Get();
+ }
+ Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
+ }
+
+ oAccess.reset();
+
+ lcl_RecalcTable( *this, nullptr, aNotify );
+
+ m_bLowersFormatted = true;
+ if ( bKeep && KEEPTAB )
+ {
+
+ // Consider case that table is inside another table,
+ // because it has to be avoided, that superior table
+ // is formatted.
+ // Thus, find next content, table or section
+ // and, if a section is found, get its first
+ // content.
+ if ( nullptr != sw_FormatNextContentForKeep( this ) && !GetNext() )
+ {
+ setFrameAreaPositionValid(false);
+ }
+ }
+ }
+ }
+ }
+
+ //Again an invalid value? - do it again...
+ if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ continue;
+
+ // check, if calculation of table frame is ready.
+
+ // Local variable <nDistanceToUpperPrtBottom>
+ // Introduce local variable and init it with the distance from the
+ // table frame bottom to the bottom of the upper printing area.
+ // Note: negative values denotes the situation that table frame doesn't fit in its upper.
+ SwTwips nDistanceToUpperPrtBottom =
+ aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
+
+ /// In online layout try to grow upper of table frame, if table frame doesn't fit in its upper.
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ if ( nDistanceToUpperPrtBottom < 0 && bBrowseMode )
+ {
+ if ( GetUpper()->Grow( -nDistanceToUpperPrtBottom ) )
+ {
+ // upper is grown --> recalculate <nDistanceToUpperPrtBottom>
+ nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
+ }
+ }
+
+ // If there is still some space left in the upper, we check if we
+ // can join some rows of the follow.
+ // Setting bLastRowHasToMoveToFollow to true means we want to force
+ // the table to be split! Only skip this if condition once.
+ if( nDistanceToUpperPrtBottom >= 0 && !bLastRowHasToMoveToFollow )
+ {
+ // If there is space left in the upper printing area, join as for trial
+ // at least one further row of an existing follow.
+ if ( !bSplit && GetFollow() )
+ {
+ bool bDummy;
+ if ( GetFollow()->ShouldBwdMoved( GetUpper(), bDummy ) )
+ {
+ SwFrame *pTmp = GetUpper();
+ SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*pTmp);
+ if ( bBrowseMode )
+ nDeadLine += pTmp->Grow( LONG_MAX, true );
+ bool bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) > 0;
+ if (!bFits && aRectFnSet.GetHeight(GetFollow()->getFrameArea()) == 0)
+ // The follow should move backwards, so allow the case
+ // when the upper has no space, but the follow is
+ // empty.
+ bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) >= 0;
+ if (bFits)
+ {
+ // First, we remove an existing follow flow line.
+ if ( HasFollowFlowLine() )
+ {
+ SwFrame* pLastLine = GetLastLower();
+ RemoveFollowFlowLine();
+ // invalidate and rebuild last row
+ if ( pLastLine )
+ {
+ ::SwInvalidateAll( pLastLine, LONG_MAX );
+ SetRebuildLastLine( true );
+ lcl_RecalcRow(*static_cast<SwRowFrame*>(pLastLine), LONG_MAX);
+ SetRebuildLastLine( false );
+ }
+
+ SwFrame* pRow = GetFollow()->GetFirstNonHeadlineRow();
+
+ if ( !pRow || !pRow->GetNext() )
+ // The follow became empty and hence useless
+ Join();
+
+ continue;
+ }
+
+ // If there is no follow flow line, we move the first
+ // row in the follow table to the master table.
+ SwRowFrame *pRow = GetFollow()->GetFirstNonHeadlineRow();
+
+ // The follow became empty and hence useless
+ if ( !pRow )
+ {
+ Join();
+ continue;
+ }
+
+ const SwTwips nOld = aRectFnSet.GetHeight(getFrameArea());
+ tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pRow );
+ SwFrame* pRowToMove = pRow;
+
+ while ( pRowToMove && nRowsToMove-- > 0 )
+ {
+ const bool bMoveFootnotes = bFootnotesInDoc && !GetFollow()->IsJoinLocked();
+
+ SwFootnoteBossFrame *pOldBoss = nullptr;
+ if ( bMoveFootnotes )
+ pOldBoss = pRowToMove->FindFootnoteBossFrame( true );
+
+ SwFrame* pNextRow = pRowToMove->GetNext();
+
+ if ( !pNextRow )
+ {
+ // The follow became empty and hence useless
+ Join();
+ }
+ else
+ {
+ pRowToMove->Cut();
+ pRowToMove->Paste( this );
+ }
+
+ // Move the footnotes!
+ if ( bMoveFootnotes )
+ if ( static_cast<SwLayoutFrame*>(pRowToMove)->MoveLowerFootnotes( nullptr, pOldBoss, FindFootnoteBossFrame( true ), true ) )
+ GetUpper()->Calc(pRenderContext);
+
+ pRowToMove = pNextRow;
+ }
+
+ if ( nOld != aRectFnSet.GetHeight(getFrameArea()) )
+ lcl_RecalcTable( *this, static_cast<SwLayoutFrame*>(pRow), aNotify );
+
+ continue;
+ }
+ }
+ }
+ else if ( KEEPTAB )
+ {
+ bool bFormat = false;
+ if ( bKeep )
+ bFormat = true;
+ else if ( bTableRowKeep && !bLastRowMoveNoMoreTries )
+ {
+ // We only want to give the last row one chance to move
+ // to the follow table. Set the flag as early as possible:
+ bLastRowMoveNoMoreTries = true;
+
+ // The last line of the table has to be cut off if:
+ // 1. The table does not want to keep with its next
+ // 2. The compatibility option is set and the table is allowed to split
+ // 3. We did not already cut off the last row
+ // 4. There is not break after attribute set at the table
+ // 5. There is no break before attribute set behind the table
+ // 6. There is no section change behind the table (see IsKeep)
+ // 7. The last table row wants to keep with its next.
+ const SwRowFrame* pLastRow = static_cast<const SwRowFrame*>(GetLastLower());
+ if (pLastRow)
+ {
+ if (!oAccess)
+ {
+ oAccess.emplace(SwFrame::GetCache(), this);
+ pAttrs = oAccess->Get();
+ }
+ if (IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), true)
+ && pLastRow->ShouldRowKeepWithNext())
+ {
+ bFormat = true;
+ }
+ }
+ }
+
+ if ( bFormat )
+ {
+ oAccess.reset();
+
+ // Consider case that table is inside another table, because
+ // it has to be avoided, that superior table is formatted.
+ // Thus, find next content, table or section and, if a section
+ // is found, get its first content.
+ const SwFrame* pTmpNxt = sw_FormatNextContentForKeep( this );
+
+ // The last row wants to keep with the frame behind the table.
+ // Check if the next frame is on a different page and valid.
+ // In this case we do a magic trick:
+ if ( !bKeep && !GetNext() && pTmpNxt && pTmpNxt->isFrameAreaDefinitionValid() )
+ {
+ setFrameAreaPositionValid(false);
+ bLastRowHasToMoveToFollow = true;
+ }
+ }
+ }
+
+ if ( isFrameAreaDefinitionValid() )
+ {
+ if (m_bCalcLowers)
+ {
+ lcl_RecalcTable( *this, nullptr, aNotify );
+ m_bLowersFormatted = true;
+ m_bCalcLowers = false;
+ }
+ else if (m_bONECalcLowers)
+ {
+ lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
+ m_bONECalcLowers = false;
+ }
+ }
+ continue;
+ }
+
+ // I don't fit in the upper Frame anymore, therefore it's the
+ // right moment to do some preferably constructive changes.
+
+ // If I'm NOT allowed to leave the upper Frame, I've got a problem.
+ // Following Arthur Dent, we do the only thing that you can do with
+ // an unsolvable problem: We ignore it with all our power.
+ if ( !bMoveable )
+ {
+ if (m_bCalcLowers && isFrameAreaDefinitionValid())
+ {
+ lcl_RecalcTable( *this, nullptr, aNotify );
+ m_bLowersFormatted = true;
+ m_bCalcLowers = false;
+ }
+ else if (m_bONECalcLowers)
+ {
+ lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
+ m_bONECalcLowers = false;
+ }
+
+ // It does not make sense to cut off the last line if we are
+ // not moveable:
+ bLastRowHasToMoveToFollow = false;
+
+ continue;
+ }
+
+ if (m_bCalcLowers && isFrameAreaDefinitionValid())
+ {
+ lcl_RecalcTable( *this, nullptr, aNotify );
+ m_bLowersFormatted = true;
+ m_bCalcLowers = false;
+ if( !isFrameAreaDefinitionValid() )
+ continue;
+ }
+
+ // First try to split the table. Condition:
+ // 1. We have at least one non headline row
+ // 2. If this row wants to keep, we need an additional row
+ // 3. The table is allowed to split or we do not have a pIndPrev:
+ SwFrame* pIndPrev = GetIndPrev();
+ const SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
+ // #i120016# if this row wants to keep, allow split in case that all rows want to keep with next,
+ // the table can not move forward as it is the first one and a split is in general allowed.
+ const bool bAllowSplitOfRow = bTableRowKeep && !pIndPrev && AreAllRowsKeepWithNext(pFirstNonHeadlineRow);
+ // tdf91083 MSCompat: this extends bAllowSplitOfRow (and perhaps should just replace it).
+ // If the kept-together items cannot move to a new page, a table split is in general allowed.
+ const bool bEmulateTableKeepSplitAllowed = bEmulateTableKeep && !IsKeepFwdMoveAllowed(/*IgnoreMyOwnKeepValue=*/true);
+
+ if ( pFirstNonHeadlineRow && nUnSplitted > 0 &&
+ ( bEmulateTableKeepSplitAllowed || bAllowSplitOfRow ||
+ ( ( !bTableRowKeep || pFirstNonHeadlineRow->GetNext() ||
+ !pFirstNonHeadlineRow->ShouldRowKeepWithNext()
+ ) && ( !bDontSplit || !pIndPrev )
+ ) ) )
+ {
+ // #i29438#
+ // Special DoNotSplit cases:
+ // We better avoid splitting of a row frame if we are inside a columned
+ // section which has a height of 0, because this is not growable and thus
+ // all kinds of unexpected things could happen.
+ if ( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() &&
+ 0 == aRectFnSet.GetHeight(GetUpper()->getFrameArea())
+ )
+ {
+ bTryToSplit = false;
+ }
+
+ // 1. Try: bTryToSplit = true => Try to split the row.
+ // 2. Try: bTryToSplit = false => Split the table between the rows.
+ if ( pFirstNonHeadlineRow->GetNext() || bTryToSplit )
+ {
+ SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
+ if( IsInSct() || GetUpper()->IsInTab() ) // TABLE IN TABLE)
+ nDeadLine = aRectFnSet.YInc( nDeadLine,
+ GetUpper()->Grow( LONG_MAX, true ) );
+
+ {
+ SwFrameDeleteGuard g(Lower()); // tdf#134965 prevent RemoveFollowFlowLine()
+ SetInRecalcLowerRow( true );
+ ::lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), nDeadLine);
+ SetInRecalcLowerRow( false );
+ }
+ m_bLowersFormatted = true;
+ aNotify.SetLowersComplete( true );
+
+ // One more check if it's really necessary to split the table.
+ // 1. The table either has to exceed the deadline or
+ // 2. We explicitly want to cut off the last row.
+ if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) > 0 && !bLastRowHasToMoveToFollow )
+ {
+ continue;
+ }
+
+ // Set to false again as early as possible.
+ bLastRowHasToMoveToFollow = false;
+
+ // #i52781#
+ // YaSC - Yet another special case:
+ // If our upper is inside a table cell which is not allowed
+ // to split, we do not try to split:
+ if ( GetUpper()->IsInTab() )
+ {
+ const SwFrame* pTmpRow = GetUpper();
+ while ( pTmpRow && !pTmpRow->IsRowFrame() )
+ pTmpRow = pTmpRow->GetUpper();
+ if ( pTmpRow && !static_cast<const SwRowFrame*>(pTmpRow)->IsRowSplitAllowed() )
+ continue;
+ }
+
+ sal_uInt16 nMinNumOfLines = nRepeat;
+
+ if ( bTableRowKeep )
+ {
+ const SwRowFrame* pTmpRow = GetFirstNonHeadlineRow();
+ while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
+ {
+ ++nMinNumOfLines;
+ pTmpRow = static_cast<const SwRowFrame*>(pTmpRow->GetNext());
+ }
+ }
+
+ if ( !bTryToSplit )
+ ++nMinNumOfLines;
+
+ const SwTwips nBreakLine = aRectFnSet.YInc(
+ aRectFnSet.GetTop(getFrameArea()),
+ aRectFnSet.GetTopMargin(*this) +
+ lcl_GetHeightOfRows( GetLower(), nMinNumOfLines ) );
+
+ // Some more checks if we want to call the split algorithm or not:
+ // The repeating lines / keeping lines still fit into the upper or
+ // if we do not have an (in)direct Prev, we split anyway.
+ if( aRectFnSet.YDiff(nDeadLine, nBreakLine) >=0
+ || !pIndPrev || bEmulateTableKeepSplitAllowed )
+ {
+ aNotify.SetLowersComplete( false );
+ bSplit = true;
+
+ // An existing follow flow line has to be removed.
+ if ( HasFollowFlowLine() )
+ {
+ if (!nThrowAwayValidLayoutLimit)
+ continue;
+ const bool bInitialLoopEndCondition(isFrameAreaDefinitionValid());
+ RemoveFollowFlowLine();
+ const bool bFinalLoopEndCondition(isFrameAreaDefinitionValid());
+
+ if (bInitialLoopEndCondition && !bFinalLoopEndCondition)
+ {
+ --nThrowAwayValidLayoutLimit;
+ }
+ }
+
+ oAccess.reset();
+ const bool bSplitError = !Split( nDeadLine, bTryToSplit, ( bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed) ) );
+
+ // tdf#130639 don't start table on a new page after the fallback "switch off repeating header"
+ if (bSplitError && nRepeat > GetTable()->GetRowsToRepeat())
+ {
+ setFrameAreaPositionValid(false);
+ break;
+ }
+
+ if (!bTryToSplit && !bSplitError)
+ {
+ --nUnSplitted;
+ }
+
+ // #i29771# Two tries to split the table
+ // If an error occurred during splitting. We start a second
+ // try, this time without splitting of table rows.
+ if ( bSplitError && HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+
+ // If splitting the table was successful or not,
+ // we do not want to have 'empty' follow tables.
+ if ( GetFollow() && !GetFollow()->GetFirstNonHeadlineRow() )
+ Join();
+
+ // We want to restore the situation before the failed
+ // split operation as good as possible. Therefore we
+ // do some more calculations. Note: Restricting this
+ // to nDeadLine may not be enough.
+ if ( bSplitError && bTryToSplit ) // no restart if we did not try to split: i72847, i79426
+ {
+ lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
+ setFrameAreaPositionValid(false);
+ bTryToSplit = false;
+ continue;
+ }
+
+ bTryToSplit = !bSplitError;
+
+ //To avoid oscillations the Follow must become valid now
+ if ( GetFollow() )
+ {
+ // #i80924#
+ // After a successful split assure that the first row
+ // is invalid. When graphics are present, this isn't hold.
+ // Note: defect i80924 could also be fixed, if it is
+ // assured, that <SwLayNotify::bLowersComplete> is only
+ // set, if all lower are valid *and* are correct laid out.
+ if ( !bSplitError && GetFollow()->GetLower() )
+ {
+ GetFollow()->GetLower()->InvalidatePos();
+ }
+ SwRectFnSet fnRectX(GetFollow());
+
+ static sal_uInt8 nStack = 0;
+ if ( !StackHack::IsLocked() && nStack < 4 )
+ {
+ ++nStack;
+ StackHack aHack;
+ oAccess.reset();
+
+ GetFollow()->MakeAll(pRenderContext);
+
+ GetFollow()->SetLowersFormatted(false);
+ // #i43913# - lock follow table
+ // to avoid its formatting during the format of
+ // its content.
+ const bool bOldJoinLock = GetFollow()->IsJoinLocked();
+ GetFollow()->LockJoin();
+ ::lcl_RecalcRow(*static_cast<SwRowFrame*>(GetFollow()->Lower()),
+ fnRectX.GetBottom(GetFollow()->GetUpper()->getFrameArea()) );
+ // #i43913#
+ // #i63632# Do not unlock the
+ // follow if it wasn't locked before.
+ if ( !bOldJoinLock )
+ GetFollow()->UnlockJoin();
+
+ if ( !GetFollow()->GetFollow() )
+ {
+ SwFrame* pNxt = static_cast<SwFrame*>(GetFollow())->FindNext();
+ if ( pNxt )
+ {
+ // #i18103# - no formatting of found next
+ // frame, if it's a follow section of the
+ // 'ColLocked' section, the follow table is
+ // in.
+ bool bCalcNxt = true;
+ if ( GetFollow()->IsInSct() && pNxt->IsSctFrame() )
+ {
+ SwSectionFrame* pSct = GetFollow()->FindSctFrame();
+ if ( pSct->IsColLocked() &&
+ pSct->GetFollow() == pNxt )
+ {
+ bCalcNxt = false;
+ }
+ }
+ if ( bCalcNxt )
+ {
+ // tdf#119109 follow was just formatted,
+ // don't do it again now
+ FlowFrameJoinLockGuard g(GetFollow());
+ pNxt->Calc(pRenderContext);
+ }
+ }
+ }
+
+ --nStack;
+ }
+ else if ( GetFollow() == GetNext() )
+ GetFollow()->MoveFwd( true, false );
+ }
+ continue;
+ }
+ }
+ }
+
+ // Set to false again as early as possible.
+ bLastRowHasToMoveToFollow = false;
+
+ if( IsInSct() && bMovedFwd && bMakePage && GetUpper()->IsColBodyFrame() &&
+ GetUpper()->GetUpper()->GetUpper()->IsSctFrame() &&
+ ( GetUpper()->GetUpper()->GetPrev() || GetIndPrev() ) &&
+ static_cast<SwSectionFrame*>(GetUpper()->GetUpper()->GetUpper())->MoveAllowed(this) )
+ {
+ bMovedFwd = false;
+ }
+
+ // #i29771# Reset bTryToSplit flag on change of upper
+ const SwFrame* pOldUpper = GetUpper();
+
+ //Let's see if we find some place anywhere...
+ if (!bMovedFwd)
+ {
+ // don't make the effort to move fwd if its known
+ // conditions that are known not to work
+ if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
+ bMakePage = false;
+ else if (!MoveFwd(bMakePage, false))
+ bMakePage = false;
+ }
+
+ // #i29771# Reset bSplitError flag on change of upper
+ if ( GetUpper() != pOldUpper )
+ {
+ bTryToSplit = true;
+ nUnSplitted = 5;
+ }
+
+ aRectFnSet.Refresh(this);
+ m_bCalcLowers = true;
+ bMovedFwd = true;
+ aNotify.SetLowersComplete( false );
+ if ( IsFollow() )
+ {
+ // To avoid oscillations, master should not remain invalid
+ SwTabFrame *pTab = FindMaster();
+ if ( pTab->GetUpper() )
+ pTab->GetUpper()->Calc(pRenderContext);
+ pTab->Calc(pRenderContext);
+ pTab->SetLowersFormatted( false );
+ }
+
+ //If my neighbour is my Follow at the same time, I'll swallow it up.
+ if ( ( GetNext() && GetNext() == GetFollow() ) || !GetLower() )
+ {
+ if ( HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+ if ( GetFollow() )
+ Join();
+ }
+
+ if ( bMovedBwd && GetUpper() )
+ {
+ //During flowing back the upper was animated to do a full repaint,
+ //we can now skip this after the whole flowing back and forth.
+ GetUpper()->ResetCompletePaint();
+ }
+
+ if (m_bCalcLowers && isFrameAreaDefinitionValid())
+ {
+ // #i44910# - format of lower frames unnecessary
+ // and can cause layout loops, if table doesn't fit and isn't
+ // allowed to split.
+ SwTwips nDistToUpperPrtBottom =
+ aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
+ if ( nDistToUpperPrtBottom >= 0 || bTryToSplit )
+ {
+ lcl_RecalcTable( *this, nullptr, aNotify );
+ m_bLowersFormatted = true;
+ m_bCalcLowers = false;
+ if (!isFramePrintAreaValid())
+ m_pTable->SetRowsToRepeat(1);
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL( "debug assertion: <SwTabFrame::MakeAll()> - format of table lowers suppressed by fix i44910" );
+ }
+#endif
+ }
+
+ } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+
+ //If my direct predecessor is my master now, it can destroy me during the
+ //next best opportunity.
+ if ( IsFollow() )
+ {
+ SwFrame *pPre = GetPrev();
+ if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
+ pPre->InvalidatePos();
+ }
+
+ m_bCalcLowers = m_bONECalcLowers = false;
+ oAccess.reset();
+ UnlockJoin();
+ if ( bMovedFwd || bMovedBwd || !bOldValidPos )
+ aNotify.SetInvaKeep();
+}
+
+static bool IsNextOnSamePage(SwPageFrame const& rPage,
+ SwTabFrame const& rTabFrame, SwTextFrame const& rAnchorFrame)
+{
+ for (SwContentFrame const* pContentFrame = rTabFrame.FindNextCnt();
+ pContentFrame && pContentFrame->FindPageFrame() == &rPage;
+ pContentFrame = pContentFrame->FindNextCnt())
+ {
+ if (pContentFrame == &rAnchorFrame)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/// Calculate the offsets arising because of FlyFrames
+bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
+ tools::Long& rLeftOffset,
+ tools::Long& rRightOffset,
+ SwTwips *const pSpaceBelowBottom) const
+{
+ bool bInvalidatePrtArea = false;
+ const SwPageFrame *pPage = FindPageFrame();
+ const SwFlyFrame* pMyFly = FindFlyFrame();
+
+ // --> #108724# Page header/footer content doesn't have to wrap around
+ // floating screen objects
+
+ const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
+ const bool bWrapAllowed = rIDSA.get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ||
+ ( !IsInFootnote() && nullptr == FindFooterOrHeader() );
+
+ if ( pPage->GetSortedObjs() && bWrapAllowed )
+ {
+ SwRectFnSet aRectFnSet(this);
+ const bool bConsiderWrapOnObjPos = rIDSA.get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);
+ tools::Long nPrtPos = aRectFnSet.GetTop(getFrameArea());
+ nPrtPos = aRectFnSet.YInc( nPrtPos, rUpper );
+ SwRect aRect( getFrameArea() );
+ if (pSpaceBelowBottom)
+ { // set to space below table frame
+ aRectFnSet.SetTopAndHeight(aRect, aRectFnSet.GetBottom(aRect), *pSpaceBelowBottom);
+ }
+ else
+ {
+ tools::Long nYDiff = aRectFnSet.YDiff( aRectFnSet.GetTop(getFramePrintArea()), rUpper );
+ if (nYDiff > 0)
+ aRectFnSet.AddBottom( aRect, -nYDiff );
+ }
+
+ bool bAddVerticalFlyOffsets = rIDSA.get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
+
+ for ( size_t i = 0; i < pPage->GetSortedObjs()->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ const SwRect aFlyRect = pFly->GetObjRectWithSpaces();
+ // #i26945# - correction of conditions,
+ // if Writer fly frame has to be considered:
+ // - no need to check, if top of Writer fly frame differs
+ // from FAR_AWAY, because it's also checked, if the Writer
+ // fly frame rectangle overlaps with <aRect>
+ // - no check, if bottom of anchor frame is prior the top of
+ // the table, because Writer fly frames can be negative positioned.
+ // - correct check, if the Writer fly frame is a lower of the
+ // table, because table lines/rows can split and an at-character
+ // anchored Writer fly frame could be positioned in the follow
+ // flow line.
+ // - add condition, that an existing anchor character text frame
+ // has to be on the same page as the table.
+ // E.g., it could happen, that the fly frame is still registered
+ // at the page frame, the table is on, but it's anchor character
+ // text frame has already changed its page.
+ const SwTextFrame* pAnchorCharFrame = pFly->FindAnchorCharFrame();
+ bool bConsiderFly =
+ // #i46807# - do not consider invalid
+ // Writer fly frames.
+ (pFly->isFrameAreaDefinitionValid() || bAddVerticalFlyOffsets) &&
+ // fly anchored at character or at paragraph
+ pFly->IsFlyAtContentFrame() &&
+ // fly overlaps with corresponding table rectangle
+ aFlyRect.Overlaps( aRect ) &&
+ // fly isn't lower of table and
+ // anchor character frame of fly isn't lower of table
+ (pSpaceBelowBottom // not if in ShouldBwdMoved
+ || (!IsAnLower( pFly ) &&
+ (!pAnchorCharFrame || !IsAnLower(pAnchorCharFrame)))) &&
+ // table isn't lower of fly
+ !pFly->IsAnLower( this ) &&
+ // fly is lower of fly, the table is in
+ // #123274# - correction
+ // assure that fly isn't a lower of a fly, the table isn't in.
+ // E.g., a table in the body doesn't wrap around a graphic,
+ // which is inside a frame.
+ ( ( !pMyFly ||
+ pMyFly->IsAnLower( pFly ) ) &&
+ pMyFly == pFly->GetAnchorFrameContainingAnchPos()->FindFlyFrame() ) &&
+ // anchor frame not on following page
+ pPage->GetPhyPageNum() >=
+ pFly->GetAnchorFrame()->FindPageFrame()->GetPhyPageNum() &&
+ // anchor character text frame on same page
+ ( !pAnchorCharFrame ||
+ pAnchorCharFrame->FindPageFrame()->GetPhyPageNum() ==
+ pPage->GetPhyPageNum() );
+
+ if ( bConsiderFly )
+ {
+ const SwFrame* pFlyHeaderFooterFrame = pFly->GetAnchorFrame()->FindFooterOrHeader();
+ const SwFrame* pThisHeaderFooterFrame = FindFooterOrHeader();
+
+ if ( pFlyHeaderFooterFrame != pThisHeaderFooterFrame &&
+ // #148493# If bConsiderWrapOnObjPos is set,
+ // we want to consider the fly if it is located in the header and
+ // the table is located in the body:
+ ( !bConsiderWrapOnObjPos || nullptr != pThisHeaderFooterFrame || !pFlyHeaderFooterFrame->IsHeaderFrame() ) )
+ bConsiderFly = false;
+ }
+
+ if ( bConsiderFly )
+ {
+ const SwFormatSurround &rSur = pFly->GetFormat()->GetSurround();
+ const SwFormatHoriOrient &rHori= pFly->GetFormat()->GetHoriOrient();
+ bool bShiftDown = css::text::WrapTextMode_NONE == rSur.GetSurround();
+ if (!bShiftDown && bAddVerticalFlyOffsets)
+ {
+ if (rSur.GetSurround() == text::WrapTextMode_PARALLEL
+ && rHori.GetHoriOrient() == text::HoriOrientation::NONE)
+ {
+ // We know that wrapping was requested and the table frame overlaps with
+ // the fly frame. Check if the print area overlaps with the fly frame as
+ // well (in case the table does not use all the available width).
+ basegfx::B1DRange aTabRange(
+ aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea()),
+ aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea())
+ + aRectFnSet.GetWidth(getFramePrintArea()));
+
+ // Ignore spacing when determining the left/right edge of the fly, like
+ // Word does.
+ const SwRect aFlyRectWithoutSpaces = pFly->GetObjRect();
+ basegfx::B1DRange aFlyRange(aRectFnSet.GetLeft(aFlyRectWithoutSpaces),
+ aRectFnSet.GetRight(aFlyRectWithoutSpaces));
+
+ // If it does, shift the table down. Do this only in the compat case,
+ // normally an SwFlyPortion is created instead that increases the height
+ // of the first table row.
+ bShiftDown = aTabRange.overlaps(aFlyRange);
+ }
+ }
+
+ if (bShiftDown)
+ {
+ // possible cases:
+ // both in body
+ // both in same fly
+ // any comb. of body, footnote, header/footer
+ // to keep it safe, check only in doc body vs page margin for now
+ tools::Long nBottom = aRectFnSet.GetBottom(aFlyRect);
+ // tdf#138039 don't grow beyond the page body
+ // if the fly is anchored below the table; the fly
+ // must move with its anchor frame to the next page
+ SwRectFnSet fnPage(pPage);
+ if (!IsInDocBody() // TODO
+ || fnPage.YDiff(fnPage.GetBottom(aFlyRect), fnPage.GetPrtBottom(*pPage)) <= 0
+ || !IsNextOnSamePage(*pPage, *this,
+ *static_cast<SwTextFrame*>(pFly->GetAnchorFrameContainingAnchPos())))
+ {
+ if (aRectFnSet.YDiff( nPrtPos, nBottom ) < 0)
+ nPrtPos = nBottom;
+ // tdf#116501 subtract flys blocking space from below
+ // TODO this may not work ideally for multiple flys
+ if (pSpaceBelowBottom
+ && aRectFnSet.YDiff(aRectFnSet.GetBottom(aRect), nBottom) < 0)
+ {
+ if (aRectFnSet.YDiff(aRectFnSet.GetTop(aRect), aRectFnSet.GetTop(aFlyRect)) < 0)
+ {
+ aRectFnSet.SetBottom(aRect, aRectFnSet.GetTop(aFlyRect));
+ }
+ else
+ {
+ aRectFnSet.SetHeight(aRect, 0);
+ }
+ }
+ bInvalidatePrtArea = true;
+ }
+ }
+ if ( (css::text::WrapTextMode_RIGHT == rSur.GetSurround() ||
+ css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&&
+ text::HoriOrientation::LEFT == rHori.GetHoriOrient() )
+ {
+ const tools::Long nWidth = aRectFnSet.XDiff(
+ aRectFnSet.GetRight(aFlyRect),
+ aRectFnSet.GetLeft(pFly->GetAnchorFrame()->getFrameArea()) );
+ rLeftOffset = std::max( rLeftOffset, nWidth );
+ bInvalidatePrtArea = true;
+ }
+ if ( (css::text::WrapTextMode_LEFT == rSur.GetSurround() ||
+ css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&&
+ text::HoriOrientation::RIGHT == rHori.GetHoriOrient() )
+ {
+ const tools::Long nWidth = aRectFnSet.XDiff(
+ aRectFnSet.GetRight(pFly->GetAnchorFrame()->getFrameArea()),
+ aRectFnSet.GetLeft(aFlyRect) );
+ rRightOffset = std::max( rRightOffset, nWidth );
+ bInvalidatePrtArea = true;
+ }
+ }
+ }
+ }
+ rUpper = aRectFnSet.YDiff( nPrtPos, aRectFnSet.GetTop(getFrameArea()) );
+ if (pSpaceBelowBottom)
+ {
+ *pSpaceBelowBottom = aRectFnSet.GetHeight(aRect);
+ }
+ }
+
+ return bInvalidatePrtArea;
+}
+
+/// "Formats" the frame; Frame and PrtArea.
+/// The fixed size is not adjusted here.
+void SwTabFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
+{
+ OSL_ENSURE( pAttrs, "TabFrame::Format, pAttrs is 0." );
+
+ SwRectFnSet aRectFnSet(this);
+ if ( !isFrameAreaSizeValid() )
+ {
+ tools::Long nDiff = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) -
+ aRectFnSet.GetWidth(getFrameArea());
+ if( nDiff )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddRight( aFrm, nDiff );
+ }
+ }
+
+ //VarSize is always the height.
+ //For the upper/lower margins the same rules apply as for ContentFrames (see
+ //MakePrtArea() of those).
+
+ SwTwips nUpper = CalcUpperSpace( pAttrs );
+
+ // We want to dodge the flys. Two possibilities:
+ // 1. There are flys with SurroundNone, dodge them completely
+ // 2. There are flys which only wrap on the right or the left side and
+ // those are right or left aligned, those set the minimum for the margins
+ tools::Long nTmpRight = -1000000,
+ nLeftOffset = 0;
+ if (CalcFlyOffsets(nUpper, nLeftOffset, nTmpRight, nullptr))
+ {
+ setFramePrintAreaValid(false);
+ }
+
+ tools::Long nRightOffset = std::max( tools::Long(0), nTmpRight );
+
+ SwTwips nLower = pAttrs->CalcBottomLine();
+ // #i29550#
+ if ( IsCollapsingBorders() )
+ nLower += GetBottomLineSize();
+
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+
+ // The width of the PrintArea is given by the FrameFormat, the margins
+ // have to be set accordingly.
+ // Minimum margins are determined depending on borders and shadows.
+ // The margins are set so that the PrintArea is aligned into the
+ // Frame according to the adjustment.
+ // If the adjustment is 0, the margins are set according to the border
+ // attributes.
+
+ const SwTwips nOldHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ const SwTwips nMax = aRectFnSet.GetWidth(getFrameArea());
+
+ // OD 14.03.2003 #i9040# - adjust variable names.
+ const SwTwips nLeftLine = pAttrs->CalcLeftLine();
+ const SwTwips nRightLine = pAttrs->CalcRightLine();
+
+ // The width possibly is a percentage value. If the table is inside
+ // something else, the value refers to the environment. If it's in the
+ // body then in the BrowseView the value refers to the screen width.
+ const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
+ // OD 14.03.2003 #i9040# - adjust variable name.
+ const SwTwips nWishedTableWidth = CalcRel( rSz );
+
+ bool bCheckBrowseWidth = false;
+
+ // OD 14.03.2003 #i9040# - insert new variables for left/right spacing.
+ SwTwips nLeftSpacing = 0;
+ SwTwips nRightSpacing = 0;
+ switch ( GetFormat()->GetHoriOrient().GetHoriOrient() )
+ {
+ case text::HoriOrientation::LEFT:
+ {
+ // left indent:
+ nLeftSpacing = nLeftLine + nLeftOffset;
+ // OD 06.03.2003 #i9040# - correct calculation of right indent:
+ // - Consider right indent given by right line attributes.
+ // - Consider negative right indent.
+ // wished right indent determined by wished table width and
+ // left offset given by surround fly frames on the left:
+ const SwTwips nWishRight = nMax - nWishedTableWidth - nLeftOffset;
+ if ( nRightOffset > 0 )
+ {
+ // surrounding fly frames on the right
+ // -> right indent is maximum of given right offset
+ // and wished right offset.
+ nRightSpacing = nRightLine + std::max( SwTwips(nRightOffset), nWishRight );
+ }
+ else
+ {
+ // no surrounding fly frames on the right
+ // If intrinsic right indent (intrinsic means not considering
+ // determined left indent) is negative,
+ // then hold this intrinsic indent,
+ // otherwise non negative wished right indent is hold.
+ nRightSpacing = nRightLine +
+ ( ( (nWishRight+nLeftOffset) < 0 ) ?
+ (nWishRight+nLeftOffset) :
+ std::max( SwTwips(0), nWishRight ) );
+ }
+ }
+ break;
+ case text::HoriOrientation::RIGHT:
+ {
+ // right indent:
+ nRightSpacing = nRightLine + nRightOffset;
+ // OD 06.03.2003 #i9040# - correct calculation of left indent:
+ // - Consider left indent given by left line attributes.
+ // - Consider negative left indent.
+ // wished left indent determined by wished table width and
+ // right offset given by surrounding fly frames on the right:
+ const SwTwips nWishLeft = nMax - nWishedTableWidth - nRightOffset;
+ if ( nLeftOffset > 0 )
+ {
+ // surrounding fly frames on the left
+ // -> right indent is maximum of given left offset
+ // and wished left offset.
+ nLeftSpacing = nLeftLine + std::max( SwTwips(nLeftOffset), nWishLeft );
+ }
+ else
+ {
+ // no surrounding fly frames on the left
+ // If intrinsic left indent (intrinsic = not considering
+ // determined right indent) is negative,
+ // then hold this intrinsic indent,
+ // otherwise non negative wished left indent is hold.
+ nLeftSpacing = nLeftLine +
+ ( ( (nWishLeft+nRightOffset) < 0 ) ?
+ (nWishLeft+nRightOffset) :
+ std::max( SwTwips(0), nWishLeft ) );
+ }
+ }
+ break;
+ case text::HoriOrientation::CENTER:
+ {
+ // OD 07.03.2003 #i9040# - consider left/right line attribute.
+ const SwTwips nCenterSpacing = ( nMax - nWishedTableWidth ) / 2;
+ nLeftSpacing = nLeftLine +
+ ( (nLeftOffset > 0) ?
+ std::max( nCenterSpacing, SwTwips(nLeftOffset) ) :
+ nCenterSpacing );
+ nRightSpacing = nRightLine +
+ ( (nRightOffset > 0) ?
+ std::max( nCenterSpacing, SwTwips(nRightOffset) ) :
+ nCenterSpacing );
+ }
+ break;
+ case text::HoriOrientation::FULL:
+ //This things grows over the whole width.
+ //Only the free space needed for the border is taken into
+ //account. The attribute values of LRSpace are ignored
+ //intentionally.
+ bCheckBrowseWidth = true;
+ nLeftSpacing = nLeftLine + nLeftOffset;
+ nRightSpacing = nRightLine + nRightOffset;
+ break;
+ case text::HoriOrientation::NONE:
+ {
+ // The margins are defined by the LRSpace attribute.
+ nLeftSpacing = pAttrs->CalcLeft( this );
+ if( nLeftOffset )
+ {
+ // OD 07.03.2003 #i9040# - surround fly frames only, if
+ // they overlap with the table.
+ // Thus, take maximum of left spacing and left offset.
+ // OD 10.03.2003 #i9040# - consider left line attribute.
+ nLeftSpacing = std::max( nLeftSpacing, SwTwips( nLeftOffset + nLeftLine ) );
+ }
+ // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
+ nRightSpacing = pAttrs->CalcRight( this );
+ if( nRightOffset )
+ {
+ // OD 07.03.2003 #i9040# - surround fly frames only, if
+ // they overlap with the table.
+ // Thus, take maximum of right spacing and right offset.
+ // OD 10.03.2003 #i9040# - consider right line attribute.
+ nRightSpacing = std::max( nRightSpacing, SwTwips( nRightOffset + nRightLine ) );
+ }
+ }
+ break;
+ case text::HoriOrientation::LEFT_AND_WIDTH:
+ {
+ // count left border and width (Word specialty)
+ // OD 10.03.2003 #i9040# - no width alignment in online mode.
+ //bCheckBrowseWidth = true;
+ nLeftSpacing = pAttrs->CalcLeft( this );
+ if( nLeftOffset )
+ {
+ // OD 10.03.2003 #i9040# - surround fly frames only, if
+ // they overlap with the table.
+ // Thus, take maximum of right spacing and right offset.
+ // OD 10.03.2003 #i9040# - consider left line attribute.
+ nLeftSpacing = std::max( nLeftSpacing, SwTwips( pAttrs->CalcLeftLine() + nLeftOffset ) );
+ }
+ // OD 10.03.2003 #i9040# - consider right and left line attribute.
+ const SwTwips nWishRight =
+ nMax - (nLeftSpacing-pAttrs->CalcLeftLine()) - nWishedTableWidth;
+ nRightSpacing = nRightLine +
+ ( (nRightOffset > 0) ?
+ std::max( nWishRight, SwTwips(nRightOffset) ) :
+ nWishRight );
+ }
+ break;
+ default:
+ OSL_FAIL( "Invalid orientation for table." );
+ }
+
+ // #i26250# - extend bottom printing area, if table
+ // is last content inside a table cell.
+ if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) &&
+ GetUpper()->IsInTab() && !GetIndNext() )
+ {
+ nLower += pAttrs->GetULSpace().GetLower();
+ }
+ aRectFnSet.SetYMargins( *this, nUpper, nLower );
+ if( (nMax - MINLAY) < (nLeftSpacing + nRightSpacing) )
+ aRectFnSet.SetXMargins( *this, 0, 0 );
+ else
+ aRectFnSet.SetXMargins( *this, nLeftSpacing, nRightSpacing );
+
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( bCheckBrowseWidth &&
+ pSh && pSh->GetViewOptions()->getBrowseMode() &&
+ GetUpper()->IsPageBodyFrame() && // only PageBodyFrames and not ColBodyFrames
+ pSh->VisArea().Width() )
+ {
+ //Don't go beyond the edge of the visible area.
+ //The page width can be bigger because objects with
+ //"over-size" are possible (RootFrame::ImplCalcBrowseWidth())
+ tools::Long nWidth = pSh->GetBrowseWidth();
+ nWidth -= getFramePrintArea().Left();
+ nWidth -= pAttrs->CalcRightLine();
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( std::min( nWidth, aPrt.Width() ) );
+ }
+
+ if ( nOldHeight != aRectFnSet.GetHeight(getFramePrintArea()) )
+ {
+ setFrameAreaSizeValid(false);
+ }
+ }
+
+ if ( isFrameAreaSizeValid() )
+ return;
+
+ setFrameAreaSizeValid(true);
+
+ // The size is defined by the content plus the margins.
+ SwTwips nRemaining = 0, nDiff;
+ SwFrame *pFrame = m_pLower;
+ while ( pFrame )
+ {
+ nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ }
+ // And now add the margins
+ nRemaining += nUpper + nLower;
+
+ nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
+ if ( nDiff > 0 )
+ Shrink( nDiff );
+ else if ( nDiff < 0 )
+ Grow( -nDiff );
+}
+
+SwTwips SwTabFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nHeight > 0 && nDist > ( LONG_MAX - nHeight ) )
+ nDist = LONG_MAX - nHeight;
+
+ if ( bTst && !IsRestrictTableGrowth() )
+ return nDist;
+
+ if ( GetUpper() )
+ {
+ //The upper only grows as far as needed. nReal provides the distance
+ //which is already available.
+ SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
+ SwFrame *pFrame = GetUpper()->Lower();
+ while ( pFrame && GetFollow() != pFrame )
+ {
+ nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ }
+
+ if ( nReal < nDist )
+ {
+ tools::Long nTmp = GetUpper()->Grow( nDist - std::max<tools::Long>(nReal, 0), bTst, bInfo );
+
+ if ( IsRestrictTableGrowth() )
+ {
+ nTmp = std::min( tools::Long(nDist), nReal + nTmp );
+ nDist = nTmp < 0 ? 0 : nTmp;
+ }
+ }
+
+ if ( !bTst )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nDist );
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ SwRect aOldFrame( getFrameArea() );
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
+ }
+#endif
+ }
+ }
+
+ if ( !bTst && ( nDist || IsRestrictTableGrowth() ) )
+ {
+ SwPageFrame *pPage = FindPageFrame();
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos_();
+ if ( GetNext()->IsContentFrame() )
+ GetNext()->InvalidatePage( pPage );
+ }
+ // #i28701# - Due to the new object positioning the
+ // frame on the next page/column can flow backward (e.g. it was moved
+ // forward due to the positioning of its objects ). Thus, invalivate this
+ // next frame, if document compatibility option 'Consider wrapping style
+ // influence on object positioning' is ON.
+ else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ InvalidateNextPos();
+ }
+ InvalidateAll_();
+ InvalidatePage( pPage );
+ SetComplete();
+
+ std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
+ const SvxGraphicPosition ePos = aBack->GetGraphicPos();
+ if ( GPOS_NONE != ePos && GPOS_TILED != ePos )
+ SetCompletePaint();
+ }
+
+ return nDist;
+}
+
+void SwTabFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SwTabFrameInvFlags eInvFlags = SwTabFrameInvFlags::NONE;
+ bool bAttrSetChg = pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which();
+
+ if(bAttrSetChg)
+ {
+ auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ SfxItemIter aOIter(*rOldSetChg.GetChgSet());
+ SfxItemIter aNIter(*rNewSetChg.GetChgSet());
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ SwAttrSetChg aOldSet(rOldSetChg);
+ SwAttrSetChg aNewSet(rNewSetChg);
+ do
+ {
+ UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
+ pNItem = aNIter.NextItem();
+ pOItem = aOIter.NextItem();
+ } while(pNItem);
+ if(aOldSet.Count() || aNewSet.Count())
+ SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ else
+ UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if(eInvFlags == SwTabFrameInvFlags::NONE)
+ return;
+
+ SwPageFrame* pPage = FindPageFrame();
+ InvalidatePage(pPage);
+ if(eInvFlags & SwTabFrameInvFlags::InvalidatePrt)
+ InvalidatePrt_();
+ if(eInvFlags & SwTabFrameInvFlags::InvalidatePos)
+ InvalidatePos_();
+ SwFrame* pTmp = GetIndNext();
+ if(nullptr != pTmp)
+ {
+ if(eInvFlags & SwTabFrameInvFlags::InvalidateIndNextPrt)
+ {
+ pTmp->InvalidatePrt_();
+ if(pTmp->IsContentFrame())
+ pTmp->InvalidatePage(pPage);
+ }
+ if(eInvFlags & SwTabFrameInvFlags::SetIndNextCompletePaint)
+ pTmp->SetCompletePaint();
+ }
+ if(eInvFlags & SwTabFrameInvFlags::InvalidatePrevPrt && nullptr != (pTmp = GetPrev()))
+ {
+ pTmp->InvalidatePrt_();
+ if(pTmp->IsContentFrame())
+ pTmp->InvalidatePage( pPage );
+ }
+ if(eInvFlags & SwTabFrameInvFlags::InvalidateBrowseWidth)
+ {
+ if(pPage && pPage->GetUpper() && !IsFollow())
+ static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
+ }
+ if(eInvFlags & SwTabFrameInvFlags::InvalidateNextPos)
+ InvalidateNextPos();
+}
+
+void SwTabFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
+ SwTabFrameInvFlags &rInvFlags,
+ SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
+{
+ bool bClear = true;
+ const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ switch( nWhich )
+ {
+ case RES_TBLHEADLINECHG:
+ if ( IsFollow() )
+ {
+ // Delete remaining headlines:
+ SwRowFrame* pLowerRow = nullptr;
+ while ( nullptr != ( pLowerRow = static_cast<SwRowFrame*>(Lower()) ) && pLowerRow->IsRepeatedHeadline() )
+ {
+ pLowerRow->Cut();
+ SwFrame::DestroyFrame(pLowerRow);
+ }
+
+ // insert new headlines
+ const sal_uInt16 nNewRepeat = GetTable()->GetRowsToRepeat();
+ auto& rLines = GetTable()->GetTabLines();
+ for ( sal_uInt16 nIdx = 0; nIdx < nNewRepeat; ++nIdx )
+ {
+ SwRowFrame* pHeadline = new SwRowFrame(*rLines[nIdx], this);
+ {
+ sw::FlyCreationSuppressor aSuppressor;
+ pHeadline->SetRepeatedHeadline(true);
+ }
+ pHeadline->Paste( this, pLowerRow );
+ }
+ }
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePrt;
+ break;
+
+ case RES_FRM_SIZE:
+ case RES_HORI_ORIENT:
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePrt | SwTabFrameInvFlags::InvalidateBrowseWidth;
+ break;
+
+ case RES_PAGEDESC: //Attribute changes (on/off)
+ if ( IsInDocBody() )
+ {
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePos;
+ SwPageFrame *pPage = FindPageFrame();
+ if (pPage)
+ {
+ if ( !GetPrev() )
+ CheckPageDescs( pPage );
+ if (GetFormat()->GetPageDesc().GetNumOffset())
+ static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
+ SwDocPosUpdate aMsgHint( pPage->getFrameArea().Top() );
+ GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ }
+ }
+ break;
+
+ case RES_BREAK:
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePos | SwTabFrameInvFlags::InvalidateNextPos;
+ break;
+
+ case RES_LAYOUT_SPLIT:
+ if ( !IsFollow() )
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePos;
+ break;
+ case RES_FRAMEDIR :
+ SetDerivedR2L( false );
+ CheckDirChange();
+ break;
+ case RES_COLLAPSING_BORDERS :
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePrt;
+ lcl_InvalidateAllLowersPrt( this );
+ break;
+ case RES_UL_SPACE:
+ rInvFlags |= SwTabFrameInvFlags::InvalidateIndNextPrt | SwTabFrameInvFlags::InvalidatePrevPrt | SwTabFrameInvFlags::SetIndNextCompletePaint;
+ [[fallthrough]];
+
+ default:
+ bClear = false;
+ }
+ if ( !bClear )
+ return;
+
+ if ( pOldSet || pNewSet )
+ {
+ if ( pOldSet )
+ pOldSet->ClearItem( nWhich );
+ if ( pNewSet )
+ pNewSet->ClearItem( nWhich );
+ }
+ else
+ {
+ SwModify aMod;
+ SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ }
+}
+
+bool SwTabFrame::GetInfo( SfxPoolItem &rHint ) const
+{
+ if ( RES_VIRTPAGENUM_INFO == rHint.Which() && IsInDocBody() && !IsFollow() )
+ {
+ SwVirtPageNumInfo &rInfo = static_cast<SwVirtPageNumInfo&>(rHint);
+ const SwPageFrame *pPage = FindPageFrame();
+ if ( pPage )
+ {
+ if ( pPage == rInfo.GetOrigPage() && !GetPrev() )
+ {
+ // Should be the one (can temporarily be different, should we be
+ // concerned about this possibility?)
+ rInfo.SetInfo( pPage, this );
+ return false;
+ }
+ if ( pPage->GetPhyPageNum() < rInfo.GetOrigPage()->GetPhyPageNum() &&
+ (!rInfo.GetPage() || pPage->GetPhyPageNum() > rInfo.GetPage()->GetPhyPageNum()))
+ {
+ //This could be the one.
+ rInfo.SetInfo( pPage, this );
+ }
+ }
+ }
+ return true;
+}
+
+SwFrame *SwTabFrame::FindLastContentOrTable()
+{
+ SwFrame *pRet = m_pLower;
+
+ while ( pRet && !pRet->IsContentFrame() )
+ {
+ SwFrame *pOld = pRet;
+
+ SwFrame *pTmp = pRet; // To skip empty section frames
+ while ( pRet->GetNext() )
+ {
+ pRet = pRet->GetNext();
+ if( !pRet->IsSctFrame() || static_cast<SwSectionFrame*>(pRet)->GetSection() )
+ pTmp = pRet;
+ }
+ pRet = pTmp;
+
+ if ( pRet->GetLower() )
+ pRet = pRet->GetLower();
+ if ( pRet == pOld )
+ {
+ // Check all other columns if there is a column based section with
+ // an empty last column at the end of the last cell - this is done
+ // by SwSectionFrame::FindLastContent
+ if( pRet->IsColBodyFrame() )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ SwSectionFrame* pSect = pRet->FindSctFrame();
+ OSL_ENSURE( pSect, "Where does this column come from?");
+ OSL_ENSURE( IsAnLower( pSect ), "Split cell?" );
+#endif
+ return pRet->FindSctFrame()->FindLastContent();
+ }
+
+ // pRet may be a cell frame without a lower (cell has been split).
+ // We have to find the last content the hard way:
+
+ OSL_ENSURE( pRet->IsCellFrame(), "SwTabFrame::FindLastContent failed" );
+ const SwFrame* pRow = pRet->GetUpper();
+ while ( pRow && !pRow->GetUpper()->IsTabFrame() )
+ pRow = pRow->GetUpper();
+ const SwContentFrame* pContentFrame = pRow ? static_cast<const SwLayoutFrame*>(pRow)->ContainsContent() : nullptr;
+ pRet = nullptr;
+
+ while ( pContentFrame && static_cast<const SwLayoutFrame*>(pRow)->IsAnLower( pContentFrame ) )
+ {
+ pRet = const_cast<SwContentFrame*>(pContentFrame);
+ pContentFrame = pContentFrame->GetNextContentFrame();
+ }
+ }
+ }
+
+ // #112929# There actually is a situation, which results in pRet = 0:
+ // Insert frame, insert table via text <-> table. This gives you a frame
+ // containing a table without any other content frames. Split the table
+ // and undo the splitting. This operation gives us a table frame without
+ // a lower.
+ if ( pRet )
+ {
+ while ( pRet->GetNext() )
+ pRet = pRet->GetNext();
+
+ if (pRet->IsSctFrame())
+ pRet = static_cast<SwSectionFrame*>(pRet)->FindLastContent();
+ }
+
+ assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet) || dynamic_cast<SwTabFrame*>(pRet));
+ return pRet;
+}
+
+SwContentFrame *SwTabFrame::FindLastContent()
+{
+ SwFrame * pRet(FindLastContentOrTable());
+
+ while (pRet && pRet->IsTabFrame()) // possibly there's only tables here!
+ { // tdf#126138 skip table, don't look inside
+ pRet = pRet->GetPrev();
+ }
+
+ assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet));
+ return static_cast<SwContentFrame*>(pRet);
+}
+
+/// Return value defines if the frm needs to be relocated
+bool SwTabFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat )
+{
+ rReformat = false;
+ if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
+ {
+ //Flowing back Frames is quite time consuming unfortunately.
+ //Most often the location where the Frame wants to flow to has the same
+ //FixSize as the Frame itself. In such a situation it's easy to check if
+ //the Frame will find enough space for its VarSize, if this is not the
+ //case, the relocation can be skipped.
+ //Checking if the Frame will find enough space is done by the Frame itself,
+ //this also takes the possibility of splitting the Frame into account.
+ //If the FixSize is different or Flys are involved (at the old or the
+ //new position) the checks are pointless, the Frame then
+ //needs to be relocated tentatively (if a bit of space is available).
+
+ //The FixSize of the environments which contain tables is always the
+ //width.
+
+ SwPageFrame *pOldPage = FindPageFrame(),
+ *pNewPage = pNewUpper->FindPageFrame();
+ bool bMoveAnyway = false;
+ SwTwips nSpace = 0;
+
+ SwRectFnSet aRectFnSet(this);
+ if ( !SwFlowFrame::IsMoveBwdJump() )
+ {
+
+ tools::Long nOldWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
+ SwRectFnSet fnRectX(pNewUpper);
+ tools::Long nNewWidth = fnRectX.GetWidth(pNewUpper->getFramePrintArea());
+ if( std::abs( nNewWidth - nOldWidth ) < 2 )
+ {
+ bMoveAnyway = BwdMoveNecessary( pOldPage, getFrameArea() ) > 1;
+ if( !bMoveAnyway )
+ {
+ SwRect aRect( pNewUpper->getFramePrintArea() );
+ aRect.Pos() += pNewUpper->getFrameArea().Pos();
+ const SwFrame *pPrevFrame = pNewUpper->Lower();
+ while ( pPrevFrame && pPrevFrame != this )
+ {
+ fnRectX.SetTop( aRect, fnRectX.GetBottom(pPrevFrame->getFrameArea()) );
+ pPrevFrame = pPrevFrame->GetNext();
+ }
+ bMoveAnyway = BwdMoveNecessary( pNewPage, aRect) > 1;
+
+ // #i54861# Due to changes made in PrepareMake,
+ // the tabfrm may not have a correct position. Therefore
+ // it is possible that pNewUpper->getFramePrintArea().Height == 0. In this
+ // case the above calculation of nSpace might give wrong
+ // results and we really do not want to MoveBackward into a
+ // 0 height frame. If nTmpSpace is already <= 0, we take this
+ // value:
+ const SwTwips nTmpSpace = fnRectX.GetHeight(aRect);
+ if ( fnRectX.GetHeight(pNewUpper->getFramePrintArea()) > 0 || nTmpSpace <= 0 )
+ nSpace = nTmpSpace;
+
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ nSpace += pNewUpper->Grow( LONG_MAX, true );
+ if (0 < nSpace && GetPrecede())
+ {
+ SwTwips nUpperDummy(0);
+ tools::Long nLeftOffsetDummy(0), nRightOffsetDummy(0);
+ // tdf#116501 check for no-wrap fly overlap
+ static_cast<const SwTabFrame*>(GetPrecede())->CalcFlyOffsets(
+ nUpperDummy, nLeftOffsetDummy, nRightOffsetDummy, &nSpace);
+ }
+ }
+ }
+ else if (!m_bLockBackMove)
+ bMoveAnyway = true;
+ }
+ else if (!m_bLockBackMove)
+ bMoveAnyway = true;
+
+ if ( bMoveAnyway )
+ {
+ rReformat = true;
+ return true;
+ }
+
+ bool bFits = nSpace > 0;
+ if (!bFits && aRectFnSet.GetHeight(getFrameArea()) == 0)
+ // This frame fits into pNewUpper in case it has no space, but this
+ // frame is empty.
+ bFits = nSpace >= 0;
+ if (!m_bLockBackMove && bFits)
+ {
+ // #i26945# - check, if follow flow line
+ // contains frame, which are moved forward due to its object
+ // positioning.
+ const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
+ if ( pFirstRow && pFirstRow->IsInFollowFlowRow() &&
+ SwLayouter::DoesRowContainMovedFwdFrame(
+ *(pFirstRow->GetFormat()->GetDoc()),
+ *pFirstRow ) )
+ {
+ return false;
+ }
+ SwTwips nTmpHeight = CalcHeightOfFirstContentLine();
+
+ // For some mysterious reason, I changed the good old
+ // 'return nHeight <= nSpace' to 'return nTmpHeight < nSpace'.
+ // This obviously results in problems with table frames in
+ // sections. Remember: Every twip is sacred.
+ return nTmpHeight <= nSpace;
+ }
+ }
+ return false;
+}
+
+void SwTabFrame::Cut()
+{
+ OSL_ENSURE( GetUpper(), "Cut without Upper()." );
+
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+ SwFrame *pFrame = GetNext();
+ if( pFrame )
+ {
+ // Possibly the old follow calculated a spacing to the predecessor
+ // which is obsolete now when it becomes the first frame
+ pFrame->InvalidatePrt_();
+ pFrame->InvalidatePos_();
+ if ( pFrame->IsContentFrame() )
+ pFrame->InvalidatePage( pPage );
+ if( IsInSct() && !GetPrev() )
+ {
+ SwSectionFrame* pSct = FindSctFrame();
+ if( !pSct->IsFollow() )
+ {
+ pSct->InvalidatePrt_();
+ pSct->InvalidatePage( pPage );
+ }
+ }
+ }
+ else
+ {
+ InvalidateNextPos();
+ //Someone has to do the retouch: predecessor or upper
+ pFrame = GetPrev();
+ if ( nullptr != pFrame )
+ {
+ pFrame->SetRetouche();
+ pFrame->Prepare( PrepareHint::WidowsOrphans );
+ pFrame->InvalidatePos_();
+ if ( pFrame->IsContentFrame() )
+ pFrame->InvalidatePage( pPage );
+ }
+ //If I am (was) the only FlowFrame in my own upper, it has to do
+ //the retouch. Moreover a new empty page might be created.
+ else
+ { SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
+ pRoot->SetSuperfluous();
+ GetUpper()->SetCompletePaint();
+ if( IsInSct() )
+ {
+ SwSectionFrame* pSct = FindSctFrame();
+ if( !pSct->IsFollow() )
+ {
+ pSct->InvalidatePrt_();
+ pSct->InvalidatePage( pPage );
+ }
+ }
+ }
+ }
+
+ //First remove, then shrink the upper.
+ SwLayoutFrame *pUp = GetUpper();
+ SwRectFnSet aRectFnSet(this);
+ RemoveFromLayout();
+ if ( pUp )
+ {
+ OSL_ENSURE( !pUp->IsFootnoteFrame(), "Table in Footnote." );
+ SwSectionFrame *pSct = nullptr;
+ // #126020# - adjust check for empty section
+ // #130797# - correct fix #126020#
+ if ( !pUp->Lower() && pUp->IsInSct() &&
+ !(pSct = pUp->FindSctFrame())->ContainsContent() &&
+ !pSct->ContainsAny( true ) )
+ {
+ if ( pUp->GetUpper() )
+ {
+ pSct->DelEmpty( false );
+ pSct->InvalidateSize_();
+ }
+ }
+ // table-in-footnote: delete empty footnote frames (like SwContentFrame::Cut)
+ else if (!pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked())
+ {
+ if (pUp->GetNext() && !pUp->GetPrev())
+ {
+ if (SwFrame *const pTmp = static_cast<SwLayoutFrame*>(pUp->GetNext())->ContainsAny())
+ {
+ pTmp->InvalidatePrt_();
+ }
+ }
+ if (!pUp->IsDeleteForbidden())
+ {
+ pUp->Cut();
+ SwFrame::DestroyFrame(pUp);
+ }
+ }
+ else if( aRectFnSet.GetHeight(getFrameArea()) )
+ {
+ // OD 26.08.2003 #i18103# - *no* 'ColUnlock' of section -
+ // undo changes of fix for #104992#
+ pUp->Shrink( getFrameArea().Height() );
+ }
+ }
+
+
+ if ( pPage && !IsFollow() && pPage->GetUpper() )
+ static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
+}
+
+void SwTabFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
+{
+ OSL_ENSURE( pParent, "No parent for pasting." );
+ OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
+ OSL_ENSURE( pParent != this, "I'm the parent myself." );
+ OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
+ OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
+ "I'm still registered somewhere." );
+
+ //Insert in the tree.
+ InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
+
+ InvalidateAll_();
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos_();
+ GetNext()->InvalidatePrt_();
+ if ( GetNext()->IsContentFrame() )
+ GetNext()->InvalidatePage( pPage );
+ }
+
+ SwRectFnSet aRectFnSet(this);
+ if( aRectFnSet.GetHeight(getFrameArea()) )
+ pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) );
+
+ if( aRectFnSet.GetWidth(getFrameArea()) != aRectFnSet.GetWidth(pParent->getFramePrintArea()) )
+ Prepare( PrepareHint::FixSizeChanged );
+ if ( GetPrev() )
+ {
+ if ( !IsFollow() )
+ {
+ GetPrev()->InvalidateSize();
+ if ( GetPrev()->IsContentFrame() )
+ GetPrev()->InvalidatePage( pPage );
+ }
+ }
+ else if ( GetNext() )
+ // Take the spacing into account when dealing with ContentFrames.
+ // There are two situations (both always happen at the same time):
+ // a) The Content becomes the first in a chain
+ // b) The new follower was previously the first in a chain
+ GetNext()->InvalidatePrt_();
+
+ if ( !pPage || IsFollow() )
+ return;
+
+ if ( pPage->GetUpper() )
+ static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
+
+ if ( !GetPrev() )//At least needed for HTML with a table at the beginning.
+ {
+ const SwPageDesc *pDesc = GetFormat()->GetPageDesc().GetPageDesc();
+ if ( (pDesc && pDesc != pPage->GetPageDesc()) ||
+ (!pDesc && pPage->GetPageDesc() != &GetFormat()->GetDoc()->GetPageDesc(0)) )
+ CheckPageDescs( pPage );
+ }
+}
+
+bool SwTabFrame::Prepare( const PrepareHint eHint, const void *, bool )
+{
+ if( PrepareHint::BossChanged == eHint )
+ CheckDirChange();
+ return false;
+}
+
+SwRowFrame::SwRowFrame(const SwTableLine &rLine, SwFrame* pSib, bool bInsertContent)
+ : SwLayoutFrame( rLine.GetFrameFormat(), pSib )
+ , m_pTabLine( &rLine )
+ , m_pFollowRow( nullptr )
+ // #i29550#
+ , mnTopMarginForLowers( 0 )
+ , mnBottomMarginForLowers( 0 )
+ , mnBottomLineSize( 0 )
+ // --> split table rows
+ , m_bIsFollowFlowRow( false )
+ // <-- split table rows
+ , m_bIsRepeatedHeadline( false )
+ , m_bIsRowSpanLine( false )
+ , m_bForceRowSplitAllowed( false )
+ , m_bIsInSplit( false )
+{
+ mnFrameType = SwFrameType::Row;
+
+ //Create the boxes and insert them.
+ const SwTableBoxes &rBoxes = rLine.GetTabBoxes();
+ SwFrame *pTmpPrev = nullptr;
+ for ( size_t i = 0; i < rBoxes.size(); ++i )
+ {
+ SwCellFrame *pNew = new SwCellFrame( *rBoxes[i], this, bInsertContent );
+ pNew->InsertBehind( this, pTmpPrev );
+ pTmpPrev = pNew;
+ }
+}
+
+void SwRowFrame::DestroyImpl()
+{
+ sw::BroadcastingModify* pMod = GetFormat();
+ if( pMod )
+ {
+ pMod->Remove( this );
+ if( !pMod->HasWriterListeners() )
+ delete pMod;
+ }
+
+ SwLayoutFrame::DestroyImpl();
+}
+
+SwRowFrame::~SwRowFrame()
+{
+}
+
+void SwRowFrame::RegistFlys( SwPageFrame *pPage )
+{
+ ::RegistFlys( pPage ? pPage : FindPageFrame(), this );
+}
+
+void SwRowFrame::OnFrameSize(const SfxPoolItem& rSize)
+{
+ SwTabFrame* pTab = FindTabFrame();
+ if(pTab)
+ {
+ const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
+ // #i35063#
+ // Invalidation required is pRow is last row
+ if(bInFirstNonHeadlineRow)
+ pTab = pTab->FindMaster();
+ if(bInFirstNonHeadlineRow || !GetNext())
+ pTab->InvalidatePos();
+ }
+ const sw::BroadcastingModify aMod;
+ SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(nullptr, &rSize));
+}
+
+void SwRowFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if(auto pNewFormatHint = dynamic_cast<const sw::TableLineFormatChanged*>(&rHint))
+ {
+ if(GetTabLine() != &pNewFormatHint->m_rTabLine)
+ return;
+ RegisterToFormat(const_cast<SwTableLineFormat&>(pNewFormatHint->m_rNewFormat));
+ InvalidateSize();
+ InvalidatePrt_();
+ SetCompletePaint();
+ ReinitializeFrameSizeAttrFlags();
+
+ // #i35063#
+ // consider 'split row allowed' attribute
+ SwTabFrame* pTab = FindTabFrame();
+ bool bInFollowFlowRow = false;
+ const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
+ if(bInFirstNonHeadlineRow ||
+ !GetNext() ||
+ (bInFollowFlowRow = IsInFollowFlowRow()) ||
+ nullptr != IsInSplitTableRow() )
+ {
+ if(bInFirstNonHeadlineRow || bInFollowFlowRow)
+ pTab = pTab->FindMaster();
+
+ pTab->SetRemoveFollowFlowLinePending(true);
+ pTab->InvalidatePos();
+ }
+ }
+ else if(auto pMoveTableLineHint = dynamic_cast<const sw::MoveTableLineHint*>(&rHint))
+ {
+
+ if(GetTabLine() != &pMoveTableLineHint->m_rTableLine)
+ return;
+ const_cast<SwFrameFormat*>(&pMoveTableLineHint->m_rNewFormat)->Add(this);
+ InvalidateAll();
+ ReinitializeFrameSizeAttrFlags();
+ return;
+ }
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(!pLegacy->m_pNew)
+ {
+ // possibly not needed?
+ SwLayoutFrame::SwClientNotify(rModify, rHint);
+ return;
+ }
+ switch(pLegacy->m_pNew->Which())
+ {
+ case RES_ATTRSET_CHG:
+ {
+ const SwAttrSet* pChgSet = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
+ const SfxPoolItem* pItem = nullptr;
+ pChgSet->GetItemState(RES_FRM_SIZE, false, &pItem);
+ if(!pItem)
+ pChgSet->GetItemState(RES_ROW_SPLIT, false, &pItem);
+ if(pItem)
+ OnFrameSize(*pItem);
+ else
+ SwLayoutFrame::SwClientNotify(rModify, rHint); // possibly not needed?
+ return;
+ }
+ case RES_FRM_SIZE:
+ case RES_ROW_SPLIT:
+ OnFrameSize(*static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew));
+ return;
+ }
+}
+
+void SwRowFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ if ( !GetNext() )
+ {
+ setFrameAreaSizeValid(false);
+ }
+
+ SwLayoutFrame::MakeAll(pRenderContext);
+}
+
+tools::Long CalcHeightWithFlys( const SwFrame *pFrame )
+{
+ SwRectFnSet aRectFnSet(pFrame);
+ tools::Long nHeight = 0;
+ const SwFrame* pTmp = pFrame->IsSctFrame() ?
+ static_cast<const SwSectionFrame*>(pFrame)->ContainsContent() : pFrame;
+ while( pTmp )
+ {
+ // #i26945# - consider follow text frames
+ const SwSortedObjs* pObjs( nullptr );
+ bool bIsFollow( false );
+ if ( pTmp->IsTextFrame() && static_cast<const SwTextFrame*>(pTmp)->IsFollow() )
+ {
+ const SwFrame* pMaster;
+ // #i46450# Master does not necessarily have
+ // to exist if this function is called from JoinFrame() ->
+ // Cut() -> Shrink()
+ const SwTextFrame* pTmpFrame = static_cast<const SwTextFrame*>(pTmp);
+ if ( pTmpFrame->GetPrev() && pTmpFrame->GetPrev()->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() &&
+ static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() != pTmp )
+ pMaster = nullptr;
+ else
+ pMaster = pTmpFrame->FindMaster();
+
+ if ( pMaster )
+ {
+ pObjs = static_cast<const SwTextFrame*>(pTmp)->FindMaster()->GetDrawObjs();
+ bIsFollow = true;
+ }
+ }
+ else
+ {
+ pObjs = pTmp->GetDrawObjs();
+ }
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ // #i26945# - if <pTmp> is follow, the
+ // anchor character frame has to be <pTmp>.
+ if ( bIsFollow &&
+ pAnchoredObj->FindAnchorCharFrame() != pTmp )
+ {
+ continue;
+ }
+ // #i26945# - consider also drawing objects
+ {
+ // OD 30.09.2003 #i18732# - only objects, which follow
+ // the text flow have to be considered.
+ const SwFrameFormat& rFrameFormat = pAnchoredObj->GetFrameFormat();
+ bool bFollowTextFlow = rFrameFormat.GetFollowTextFlow().GetValue();
+ bool bIsFarAway = pAnchoredObj->GetObjRect().Top() != FAR_AWAY;
+ const SwPageFrame* pPageFrm = pTmp->FindPageFrame();
+ bool bIsAnchoredToTmpFrm = false;
+ if ( pPageFrm && pPageFrm->IsPageFrame() && pAnchoredObj->GetPageFrame())
+ bIsAnchoredToTmpFrm = pAnchoredObj->GetPageFrame() == pPageFrm ||
+ (pPageFrm->GetFormatPage().GetPhyPageNum() == pAnchoredObj->GetPageFrame()->GetFormatPage().GetPhyPageNum() + 1);
+ const bool bConsiderObj =
+ (rFrameFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) &&
+ bIsFarAway &&
+ bFollowTextFlow && bIsAnchoredToTmpFrm;
+ bool bWrapThrough = rFrameFormat.GetSurround().GetValue() == text::WrapTextMode_THROUGH;
+ bool bInBackground = !rFrameFormat.GetOpaque().GetValue();
+ if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough && bInBackground)
+ {
+ // Ignore wrap-through objects when determining the cell height.
+ // Normally FollowTextFlow requires a resize of the cell, but not in case of
+ // wrap-through.
+ continue;
+ }
+
+ if ( bConsiderObj )
+ {
+ const SwFormatFrameSize &rSz = rFrameFormat.GetFrameSize();
+ if( !rSz.GetHeightPercent() )
+ {
+ const SwTwips nDistOfFlyBottomToAnchorTop =
+ aRectFnSet.GetHeight(pAnchoredObj->GetObjRect()) +
+ ( aRectFnSet.IsVert() ?
+ pAnchoredObj->GetCurrRelPos().X() :
+ pAnchoredObj->GetCurrRelPos().Y() );
+
+ const SwTwips nFrameDiff =
+ aRectFnSet.YDiff(
+ aRectFnSet.GetTop(pTmp->getFrameArea()),
+ aRectFnSet.GetTop(pFrame->getFrameArea()) );
+
+ nHeight = std::max( nHeight, nDistOfFlyBottomToAnchorTop + nFrameDiff -
+ aRectFnSet.GetHeight(pFrame->getFrameArea()) );
+
+ // #i56115# The first height calculation
+ // gives wrong results if pFrame->getFramePrintArea().Y() > 0. We do
+ // a second calculation based on the actual rectangles of
+ // pFrame and pAnchoredObj, and use the maximum of the results.
+ // I do not want to remove the first calculation because
+ // if clipping has been applied, using the GetCurrRelPos
+ // might be the better option to calculate nHeight.
+ const SwTwips nDistOfFlyBottomToAnchorTop2 = aRectFnSet.YDiff(
+ aRectFnSet.GetBottom(pAnchoredObj->GetObjRect()),
+ aRectFnSet.GetBottom(pFrame->getFrameArea()) );
+
+ nHeight = std::max( nHeight, tools::Long(nDistOfFlyBottomToAnchorTop2 ));
+ }
+ }
+ }
+ }
+ }
+ if( !pFrame->IsSctFrame() )
+ break;
+ pTmp = pTmp->FindNextCnt();
+ if( !static_cast<const SwSectionFrame*>(pFrame)->IsAnLower( pTmp ) )
+ break;
+ }
+ return nHeight;
+}
+
+static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame& rCell, const SwBorderAttrs& rAttrs )
+{
+ const SwTabFrame* pTab = rCell.FindTabFrame();
+ SwTwips nTopSpace = 0;
+ SwTwips nBottomSpace = 0;
+
+ // #i29550#
+ if ( pTab->IsCollapsingBorders() && rCell.Lower() && !rCell.Lower()->IsRowFrame() )
+ {
+ nTopSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetTopMarginForLowers();
+ nBottomSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetBottomMarginForLowers();
+ }
+ else
+ {
+ if ( pTab->IsVertical() != rCell.IsVertical() )
+ {
+ nTopSpace = rAttrs.CalcLeft( &rCell );
+ nBottomSpace = rAttrs.CalcRight( &rCell );
+ }
+ else
+ {
+ nTopSpace = rAttrs.CalcTop();
+ nBottomSpace = rAttrs.CalcBottom();
+ }
+ }
+
+ return nTopSpace + nBottomSpace;
+}
+
+// #i26945# - add parameter <_bConsiderObjs> in order to
+// control, if floating screen objects have to be considered for the minimal
+// cell height.
+static SwTwips lcl_CalcMinCellHeight( const SwLayoutFrame *_pCell,
+ const bool _bConsiderObjs,
+ const SwBorderAttrs *pAttrs = nullptr )
+{
+ SwRectFnSet aRectFnSet(_pCell);
+ SwTwips nHeight = 0;
+ const SwFrame* pLow = _pCell->Lower();
+ if ( pLow )
+ {
+ tools::Long nFlyAdd = 0;
+ while ( pLow )
+ {
+ if ( pLow->IsRowFrame() )
+ {
+ // #i26945#
+ nHeight += ::lcl_CalcMinRowHeight( static_cast<const SwRowFrame*>(pLow),
+ _bConsiderObjs );
+ }
+ else
+ {
+ tools::Long nLowHeight = aRectFnSet.GetHeight(pLow->getFrameArea());
+ nHeight += nLowHeight;
+ // #i26945#
+ if ( _bConsiderObjs )
+ {
+ nFlyAdd = std::max( tools::Long(0), nFlyAdd - nLowHeight );
+ nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) );
+ }
+ }
+
+ pLow = pLow->GetNext();
+ }
+ if ( nFlyAdd )
+ nHeight += nFlyAdd;
+ }
+ // The border/margin needs to be considered too, unfortunately it can't be
+ // calculated using PrintArea and FrameArea because any or all of those
+ // may be invalid.
+ if ( _pCell->Lower() )
+ {
+ if ( pAttrs )
+ nHeight += lcl_CalcTopAndBottomMargin( *_pCell, *pAttrs );
+ else
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), _pCell );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ nHeight += lcl_CalcTopAndBottomMargin( *_pCell, rAttrs );
+ }
+ }
+ return nHeight;
+}
+
+// #i26945# - add parameter <_bConsiderObjs> in order to control,
+// if floating screen objects have to be considered for the minimal cell height
+static SwTwips lcl_CalcMinRowHeight( const SwRowFrame* _pRow,
+ const bool _bConsiderObjs )
+{
+ SwTwips nHeight = 0;
+ if ( !_pRow->IsRowSpanLine() )
+ {
+ const SwFormatFrameSize &rSz = _pRow->GetFormat()->GetFrameSize();
+ if ( _pRow->HasFixSize() )
+ {
+ OSL_ENSURE(SwFrameSize::Fixed == rSz.GetHeightSizeType(), "pRow claims to have fixed size");
+ return rSz.GetHeight();
+ }
+ // If this row frame is being split, then row's minimal height shouldn't restrict
+ // this frame's minimal height, because the rest will go to follow frame.
+ else if ( !_pRow->IsInSplit() && rSz.GetHeightSizeType() == SwFrameSize::Minimum )
+ {
+ nHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*_pRow);
+ }
+ }
+
+ SwRectFnSet aRectFnSet(_pRow);
+ const SwCellFrame* pLow = static_cast<const SwCellFrame*>(_pRow->Lower());
+ while ( pLow )
+ {
+ SwTwips nTmp = 0;
+ const tools::Long nRowSpan = pLow->GetLayoutRowSpan();
+ // --> NEW TABLES
+ // Consider height of
+ // 1. current cell if RowSpan == 1
+ // 2. current cell if cell is "follow" cell of a cell with RowSpan == -1
+ // 3. master cell if RowSpan == -1
+ if ( 1 == nRowSpan )
+ {
+ nTmp = ::lcl_CalcMinCellHeight( pLow, _bConsiderObjs );
+ }
+ else if ( -1 == nRowSpan )
+ {
+ // Height of the last cell of a row span is height of master cell
+ // minus the height of the other rows which are covered by the master
+ // cell:
+ const SwCellFrame& rMaster = pLow->FindStartEndOfRowSpanCell( true );
+ nTmp = ::lcl_CalcMinCellHeight( &rMaster, _bConsiderObjs );
+ const SwFrame* pMasterRow = rMaster.GetUpper();
+ while ( pMasterRow && pMasterRow != _pRow )
+ {
+ nTmp -= aRectFnSet.GetHeight(pMasterRow->getFrameArea());
+ pMasterRow = pMasterRow->GetNext();
+ }
+ }
+ // <-- NEW TABLES
+
+ // Do not consider rotated cells:
+ if ( pLow->IsVertical() == aRectFnSet.IsVert() && nTmp > nHeight )
+ nHeight = nTmp;
+
+ pLow = static_cast<const SwCellFrame*>(pLow->GetNext());
+ }
+
+ return nHeight;
+}
+
+// #i29550#
+
+// Calculate the maximum of (TopLineSize + TopLineDist) over all lowers:
+static sal_uInt16 lcl_GetTopSpace( const SwRowFrame& rRow )
+{
+ sal_uInt16 nTopSpace = 0;
+ for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
+ pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
+ {
+ sal_uInt16 nTmpTopSpace = 0;
+ if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
+ nTmpTopSpace = lcl_GetTopSpace( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
+ else
+ {
+ const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
+ const SvxBoxItem& rBoxItem = rSet.GetBox();
+ nTmpTopSpace = rBoxItem.CalcLineSpace( SvxBoxItemLine::TOP, true );
+ }
+ nTopSpace = std::max( nTopSpace, nTmpTopSpace );
+ }
+ return nTopSpace;
+}
+
+// Calculate the maximum of TopLineDist over all lowers:
+static sal_uInt16 lcl_GetTopLineDist( const SwRowFrame& rRow )
+{
+ sal_uInt16 nTopLineDist = 0;
+ for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
+ pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
+ {
+ sal_uInt16 nTmpTopLineDist = 0;
+ if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
+ nTmpTopLineDist = lcl_GetTopLineDist( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
+ else
+ {
+ const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
+ const SvxBoxItem& rBoxItem = rSet.GetBox();
+ nTmpTopLineDist = rBoxItem.GetDistance( SvxBoxItemLine::TOP );
+ }
+ nTopLineDist = std::max( nTopLineDist, nTmpTopLineDist );
+ }
+ return nTopLineDist;
+}
+
+// Calculate the maximum of BottomLineSize over all lowers:
+static sal_uInt16 lcl_GetBottomLineSize( const SwRowFrame& rRow )
+{
+ sal_uInt16 nBottomLineSize = 0;
+ for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
+ pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
+ {
+ sal_uInt16 nTmpBottomLineSize = 0;
+ if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
+ {
+ const SwFrame* pRow = pCurrLower->GetLastLower();
+ nTmpBottomLineSize = lcl_GetBottomLineSize( *static_cast<const SwRowFrame*>(pRow) );
+ }
+ else
+ {
+ const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
+ const SvxBoxItem& rBoxItem = rSet.GetBox();
+ nTmpBottomLineSize = rBoxItem.CalcLineSpace( SvxBoxItemLine::BOTTOM, true ) -
+ rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
+ }
+ nBottomLineSize = std::max( nBottomLineSize, nTmpBottomLineSize );
+ }
+ return nBottomLineSize;
+}
+
+// Calculate the maximum of BottomLineDist over all lowers:
+static sal_uInt16 lcl_GetBottomLineDist( const SwRowFrame& rRow )
+{
+ sal_uInt16 nBottomLineDist = 0;
+ for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
+ pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
+ {
+ sal_uInt16 nTmpBottomLineDist = 0;
+ if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
+ {
+ const SwFrame* pRow = pCurrLower->GetLastLower();
+ nTmpBottomLineDist = lcl_GetBottomLineDist( *static_cast<const SwRowFrame*>(pRow) );
+ }
+ else
+ {
+ const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
+ const SvxBoxItem& rBoxItem = rSet.GetBox();
+ nTmpBottomLineDist = rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
+ }
+ nBottomLineDist = std::max( nBottomLineDist, nTmpBottomLineDist );
+ }
+ return nBottomLineDist;
+}
+
+// tdf#104425: calculate the height of all row frames,
+// for which this frame is a follow.
+// When a row has fixed/minimum height, it may span over
+// several pages. The minimal height on this page should
+// take into account the sum of all the heights of previous
+// frames that constitute the table row on previous pages.
+// Otherwise, trying to split a too high row frame will
+// result in loop trying to create that too high row
+// on each following page
+static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow)
+{
+ // We don't need to account for previous instances of repeated headlines
+ if (rRow.IsRepeatedHeadline())
+ return 0;
+ SwRectFnSet aRectFnSet(&rRow);
+ const SwTableLine* pLine = rRow.GetTabLine();
+ const SwTabFrame* pTab = rRow.FindTabFrame();
+ if (!pLine || !pTab || !pTab->IsFollow())
+ return 0;
+ SwTwips nResult = 0;
+ SwIterator<SwRowFrame, SwFormat> aIter(*pLine->GetFrameFormat());
+ for (const SwRowFrame* pCurRow = aIter.First(); pCurRow; pCurRow = aIter.Next())
+ {
+ if (pCurRow != &rRow && pCurRow->GetTabLine() == pLine)
+ {
+ // We've found another row frame that is part of the same table row
+ const SwTabFrame* pCurTab = pCurRow->FindTabFrame();
+ // A row frame may not belong to a table frame, when it is being cut, e.g., in
+ // lcl_PostprocessRowsInCells().
+ // Its SwRowFrame::Cut() has been called; it in turn called SwLayoutFrame::Cut(),
+ // which nullified row's upper in RemoveFromLayout(), and then called Shrink()
+ // for its former upper.
+ // Regardless of whether it will be pasted back, or destroyed, currently it's not
+ // part of layout, and its height does not count
+ if (pCurTab && pCurTab->IsAnFollow(pTab))
+ {
+ // The found row frame belongs to a table frame that precedes
+ // (above) this one in chain. So, include it in the sum
+ nResult += aRectFnSet.GetHeight(pCurRow->getFrameArea());
+ }
+ }
+ }
+ return nResult;
+}
+
+void SwRowFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
+{
+ SwRectFnSet aRectFnSet(this);
+ OSL_ENSURE( pAttrs, "SwRowFrame::Format without Attrs." );
+
+ const bool bFix = mbFixSize;
+
+ if ( !isFramePrintAreaValid() )
+ {
+ // RowFrames don't have borders/margins therefore the PrintArea always
+ // matches the FrameArea.
+ setFramePrintAreaValid(true);
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Left( 0 );
+ aPrt.Top( 0 );
+ aPrt.Width ( getFrameArea().Width() );
+ aPrt.Height( getFrameArea().Height() );
+ }
+
+ // #i29550#
+ // Here we calculate the top-printing area for the lower cell frames
+ SwTabFrame* pTabFrame = FindTabFrame();
+ if ( pTabFrame->IsCollapsingBorders() )
+ {
+ const sal_uInt16 nTopSpace = lcl_GetTopSpace( *this );
+ const sal_uInt16 nTopLineDist = lcl_GetTopLineDist( *this );
+ const sal_uInt16 nBottomLineSize = lcl_GetBottomLineSize( *this );
+ const sal_uInt16 nBottomLineDist = lcl_GetBottomLineDist( *this );
+
+ const SwRowFrame* pPreviousRow = nullptr;
+
+ // #i32456#
+ // In order to calculate the top printing area for the lower cell
+ // frames, we have to find the 'previous' row frame and compare
+ // the bottom values of the 'previous' row with the 'top' values
+ // of this row. The best way to find the 'previous' row is to
+ // use the table structure:
+ const SwTable* pTable = pTabFrame->GetTable();
+ const SwTableLine* pPrevTabLine = nullptr;
+ const SwRowFrame* pTmpRow = this;
+
+ while ( pTmpRow && !pPrevTabLine )
+ {
+ size_t nIdx = 0;
+ const SwTableLines& rLines = pTmpRow->GetTabLine()->GetUpper() ?
+ pTmpRow->GetTabLine()->GetUpper()->GetTabLines() :
+ pTable->GetTabLines();
+
+ while ( rLines[ nIdx ] != pTmpRow->GetTabLine() )
+ ++nIdx;
+
+ if ( nIdx > 0 )
+ {
+ // pTmpRow has a 'previous' row in the table structure:
+ pPrevTabLine = rLines[ nIdx - 1 ];
+ }
+ else
+ {
+ // pTmpRow is a first row in the table structure.
+ // We go up in the table structure:
+ pTmpRow = pTmpRow->GetUpper()->GetUpper() &&
+ pTmpRow->GetUpper()->GetUpper()->IsRowFrame() ?
+ static_cast<const SwRowFrame*>( pTmpRow->GetUpper()->GetUpper() ) :
+ nullptr;
+ }
+ }
+
+ // If we found a 'previous' row, we look for the appropriate row frame:
+ if ( pPrevTabLine )
+ {
+ SwIterator<SwRowFrame,SwFormat> aIter( *pPrevTabLine->GetFrameFormat() );
+ for ( SwRowFrame* pRow = aIter.First(); pRow; pRow = aIter.Next() )
+ {
+ // #115759# - do *not* take repeated
+ // headlines, because during split of table it can be
+ // invalid and thus can't provide correct border values.
+ if ( pRow->GetTabLine() == pPrevTabLine &&
+ !pRow->IsRepeatedHeadline() )
+ {
+ pPreviousRow = pRow;
+ break;
+ }
+ }
+ }
+
+ sal_uInt16 nTopPrtMargin = nTopSpace;
+ if ( pPreviousRow )
+ {
+ const sal_uInt16 nTmpPrtMargin = pPreviousRow->GetBottomLineSize() + nTopLineDist;
+ if ( nTmpPrtMargin > nTopPrtMargin )
+ nTopPrtMargin = nTmpPrtMargin;
+ }
+
+ // table has to be notified if it has to change its lower
+ // margin due to changes of nBottomLineSize:
+ if ( !GetNext() && nBottomLineSize != GetBottomLineSize() )
+ pTabFrame->InvalidatePrt_();
+
+ // If there are rows nested inside this row, the nested rows
+ // may not have been calculated yet. Therefore the
+ // ::lcl_CalcMinRowHeight( this ) operation later in this
+ // function cannot consider the correct border values. We
+ // have to trigger the invalidation of the outer row frame
+ // manually:
+ // Note: If any further invalidations should be necessary, we
+ // should consider moving the invalidation stuff to the
+ // appropriate SwNotify object.
+ if ( GetUpper()->GetUpper()->IsRowFrame() &&
+ ( nBottomLineDist != GetBottomMarginForLowers() ||
+ nTopPrtMargin != GetTopMarginForLowers() ) )
+ GetUpper()->GetUpper()->InvalidateSize_();
+
+ SetBottomMarginForLowers( nBottomLineDist ); // 3.
+ SetBottomLineSize( nBottomLineSize ); // 4.
+ SetTopMarginForLowers( nTopPrtMargin ); // 5.
+
+ }
+ }
+
+ while ( !isFrameAreaSizeValid() )
+ {
+ setFrameAreaSizeValid(true);
+
+#if OSL_DEBUG_LEVEL > 0
+ if ( HasFixSize() )
+ {
+ const SwFormatFrameSize &rFrameSize = GetFormat()->GetFrameSize();
+ OSL_ENSURE( rFrameSize.GetSize().Height() > 0, "Has it" );
+ }
+#endif
+ const SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) -
+ ( HasFixSize() && !IsRowSpanLine()
+ ? pAttrs->GetSize().Height()
+ // #i26945#
+ : ::lcl_CalcMinRowHeight( this,
+ FindTabFrame()->IsConsiderObjsForMinCellHeight() ) );
+ if ( nDiff )
+ {
+ mbFixSize = false;
+ if ( nDiff > 0 )
+ Shrink( nDiff, false, true );
+ else if ( nDiff < 0 )
+ Grow( -nDiff );
+ mbFixSize = bFix;
+ }
+ }
+
+ // last row will fill the space in its upper.
+ if ( GetNext() )
+ return;
+
+ //The last fills the remaining space in the upper.
+ SwTwips nDiff = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
+ SwFrame *pSibling = GetUpper()->Lower();
+ do
+ { nDiff -= aRectFnSet.GetHeight(pSibling->getFrameArea());
+ pSibling = pSibling->GetNext();
+ } while ( pSibling );
+ if ( nDiff > 0 )
+ {
+ mbFixSize = false;
+ Grow( nDiff );
+ mbFixSize = bFix;
+ setFrameAreaSizeValid(true);
+ }
+}
+
+void SwRowFrame::AdjustCells( const SwTwips nHeight, const bool bHeight )
+{
+ SwFrame *pFrame = Lower();
+ if ( bHeight )
+ {
+ SwRectFnSet aRectFnSet(this);
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwRect aOldFrame;
+#endif
+
+ while ( pFrame )
+ {
+ SwFrame* pNotify = nullptr;
+
+ SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pFrame);
+
+ // NEW TABLES
+ // Which cells need to be adjusted if the current row changes
+ // its height?
+
+ // Current frame is a covered frame:
+ // Set new height for covered cell and adjust master cell:
+ if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
+ {
+ // Set height of current (covered) cell to new line height.
+ const tools::Long nDiff = nHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
+ if ( nDiff )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCellFrame);
+ aRectFnSet.AddBottom( aFrm, nDiff );
+ }
+
+ pCellFrame->InvalidatePrt_();
+ }
+ }
+
+ SwCellFrame* pToAdjust = nullptr;
+ SwFrame* pToAdjustRow = nullptr;
+
+ // If current frame is covered frame, we still want to adjust the
+ // height of the cell starting the row span
+ if ( pCellFrame->GetLayoutRowSpan() < 1 )
+ {
+ pToAdjust = const_cast< SwCellFrame*>(&pCellFrame->FindStartEndOfRowSpanCell( true ));
+ pToAdjustRow = pToAdjust->GetUpper();
+ }
+ else
+ {
+ pToAdjust = pCellFrame;
+ pToAdjustRow = this;
+ }
+
+ // Set height of master cell to height of all lines spanned by this line.
+ tools::Long nRowSpan = pToAdjust->GetLayoutRowSpan();
+ SwTwips nSumRowHeight = 0;
+ while ( pToAdjustRow )
+ {
+ // Use new height for the current row:
+ nSumRowHeight += pToAdjustRow == this ?
+ nHeight :
+ aRectFnSet.GetHeight(pToAdjustRow->getFrameArea());
+
+ if ( nRowSpan-- == 1 )
+ break;
+
+ pToAdjustRow = pToAdjustRow->GetNext();
+ }
+
+ if ( pToAdjustRow && pToAdjustRow != this )
+ pToAdjustRow->InvalidateSize_();
+
+ const tools::Long nDiff = nSumRowHeight - aRectFnSet.GetHeight(pToAdjust->getFrameArea());
+ if ( nDiff )
+ {
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ aOldFrame = pToAdjust->getFrameArea();
+#endif
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pToAdjust);
+ aRectFnSet.AddBottom( aFrm, nDiff );
+ pNotify = pToAdjust;
+ }
+
+ if ( pNotify )
+ {
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() && pRootFrame->GetCurrShell() )
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pNotify, aOldFrame );
+#endif
+
+ pNotify->InvalidatePrt_();
+ }
+
+ pFrame = pFrame->GetNext();
+ }
+ }
+ else
+ { while ( pFrame )
+ {
+ pFrame->InvalidateAll_();
+ pFrame = pFrame->GetNext();
+ }
+ }
+ InvalidatePage();
+}
+
+void SwRowFrame::Cut()
+{
+ SwTabFrame *pTab = FindTabFrame();
+ if ( pTab && pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow() )
+ {
+ pTab->FindMaster()->InvalidatePos();
+ }
+
+ SwLayoutFrame::Cut();
+}
+
+SwTwips SwRowFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwTwips nReal = 0;
+
+ SwTabFrame* pTab = FindTabFrame();
+ SwRectFnSet aRectFnSet(pTab);
+
+ bool bRestrictTableGrowth;
+ bool bHasFollowFlowLine = pTab->HasFollowFlowLine();
+
+ if ( GetUpper()->IsTabFrame() )
+ {
+ const SwRowFrame* pFollowFlowRow = IsInSplitTableRow();
+ bRestrictTableGrowth = pFollowFlowRow && !pFollowFlowRow->IsRowSpanLine();
+ }
+ else
+ {
+ OSL_ENSURE( GetUpper()->IsCellFrame(), "RowFrame->GetUpper neither table nor cell" );
+ bRestrictTableGrowth = GetFollowRow() && bHasFollowFlowLine;
+ OSL_ENSURE( !bRestrictTableGrowth || !GetNext(),
+ "GetFollowRow for row frame that has a Next" );
+
+ // There may still be some space left in my direct upper:
+ const SwTwips nAdditionalSpace =
+ aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()->GetUpper()) );
+ if ( bRestrictTableGrowth && nAdditionalSpace > 0 )
+ {
+ nReal = std::min( nAdditionalSpace, nDist );
+ nDist -= nReal;
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nReal );
+ }
+ }
+ }
+
+ if ( bRestrictTableGrowth )
+ pTab->SetRestrictTableGrowth( true );
+ else
+ {
+ // Ok, this looks like a hack, indeed, it is a hack.
+ // If the current row frame is inside another cell frame,
+ // and the current row frame has no follow, it should not
+ // be allowed to grow. In fact, setting bRestrictTableGrowth
+ // to 'false' does not work, because the surrounding RowFrame
+ // would set this to 'true'.
+ pTab->SetFollowFlowLine( false );
+ }
+
+ nReal += SwLayoutFrame::GrowFrame( nDist, bTst, bInfo);
+
+ pTab->SetRestrictTableGrowth( false );
+ pTab->SetFollowFlowLine( bHasFollowFlowLine );
+
+ //Update the height of the cells to the newest value.
+ if ( !bTst )
+ {
+ SwRectFnSet fnRectX(this);
+ AdjustCells( fnRectX.GetHeight(getFramePrintArea()) + nReal, true );
+ if ( nReal )
+ SetCompletePaint();
+ }
+
+ return nReal;
+}
+
+SwTwips SwRowFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwRectFnSet aRectFnSet(this);
+ if( HasFixSize() )
+ {
+ AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()), true );
+ return 0;
+ }
+
+ // bInfo may be set to true by SwRowFrame::Format; we need to handle this
+ // here accordingly
+ const bool bShrinkAnyway = bInfo;
+
+ //Only shrink as much as the content of the biggest cell allows.
+ SwTwips nRealDist = nDist;
+ SwFormat* pMod = GetFormat();
+ if (pMod)
+ {
+ const SwFormatFrameSize &rSz = pMod->GetFrameSize();
+ SwTwips nMinHeight = 0;
+ if (rSz.GetHeightSizeType() == SwFrameSize::Minimum)
+ nMinHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*this),
+ tools::Long(0));
+
+ // Only necessary to calculate minimal row height if height
+ // of pRow is at least nMinHeight. Otherwise nMinHeight is the
+ // minimum height.
+ if( nMinHeight < aRectFnSet.GetHeight(getFrameArea()) )
+ {
+ // #i26945#
+ OSL_ENSURE( FindTabFrame(), "<SwRowFrame::ShrinkFrame(..)> - no table frame -> crash." );
+ const bool bConsiderObjs( FindTabFrame()->IsConsiderObjsForMinCellHeight() );
+ nMinHeight = lcl_CalcMinRowHeight( this, bConsiderObjs );
+ }
+
+ if ( (aRectFnSet.GetHeight(getFrameArea()) - nRealDist) < nMinHeight )
+ nRealDist = aRectFnSet.GetHeight(getFrameArea()) - nMinHeight;
+ }
+ if ( nRealDist < 0 )
+ nRealDist = 0;
+
+ SwTwips nReal = nRealDist;
+ if ( nReal )
+ {
+ if ( !bTst )
+ {
+ SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nHeight - nReal );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nReal );
+ }
+ }
+
+ SwLayoutFrame* pFrame = GetUpper();
+ SwTwips nTmp = pFrame ? pFrame->Shrink(nReal, bTst) : 0;
+ if ( !bShrinkAnyway && !GetNext() && nTmp != nReal )
+ {
+ //The last one gets the leftover in the upper and therefore takes
+ //care (otherwise: endless loop)
+ if ( !bTst )
+ {
+ nReal -= nTmp;
+ SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nHeight + nReal );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX( -nReal );
+ }
+ }
+ nReal = nTmp;
+ }
+ }
+
+ // Invalidate appropriately and update the height to the newest value.
+ if ( !bTst )
+ {
+ if ( nReal )
+ {
+ if ( GetNext() )
+ GetNext()->InvalidatePos_();
+ InvalidateAll_();
+ SetCompletePaint();
+
+ SwTabFrame *pTab = FindTabFrame();
+ if ( !pTab->IsRebuildLastLine()
+ && pTab->IsFollow()
+ && this == pTab->GetFirstNonHeadlineRow()
+ && !pTab->IsInRecalcLowerRow() )
+ {
+ SwTabFrame* pMasterTab = pTab->FindMaster();
+ pMasterTab->InvalidatePos();
+ }
+ }
+ AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()) - nReal, true );
+ }
+ return nReal;
+}
+
+bool SwRowFrame::IsRowSplitAllowed() const
+{
+ // Fixed size rows are never allowed to split:
+ if ( HasFixSize() )
+ {
+ OSL_ENSURE( SwFrameSize::Fixed == GetFormat()->GetFrameSize().GetHeightSizeType(), "pRow claims to have fixed size" );
+ return false;
+ }
+
+ // Repeated headlines are never allowed to split:
+ const SwTabFrame* pTabFrame = FindTabFrame();
+ if ( pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
+ pTabFrame->IsInHeadline( *this ) )
+ return false;
+
+ if ( IsForceRowSplitAllowed() )
+ return true;
+
+ const SwTableLineFormat* pFrameFormat = static_cast<SwTableLineFormat*>(GetTabLine()->GetFrameFormat());
+ const SwFormatRowSplit& rLP = pFrameFormat->GetRowSplit();
+ return rLP.GetValue();
+}
+
+bool SwRowFrame::ShouldRowKeepWithNext( const bool bCheckParents ) const
+{
+ // No KeepWithNext if nested in another table
+ if ( GetUpper()->GetUpper()->IsCellFrame() )
+ return false;
+
+ const SwCellFrame* pCell = static_cast<const SwCellFrame*>(Lower());
+ const SwFrame* pText = pCell->Lower();
+
+ return pText && pText->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pText)->GetTextNodeForParaProps()->GetSwAttrSet().GetKeep(bCheckParents).GetValue();
+}
+
+SwCellFrame::SwCellFrame(const SwTableBox &rBox, SwFrame* pSib, bool bInsertContent)
+ : SwLayoutFrame( rBox.GetFrameFormat(), pSib )
+ , m_pTabBox( &rBox )
+{
+ mnFrameType = SwFrameType::Cell;
+
+ if ( !bInsertContent )
+ return;
+
+ //If a StartIdx is available, ContentFrames are added in the cell, otherwise
+ //Rows have to be present and those are added.
+ if ( rBox.GetSttIdx() )
+ {
+ SwNodeOffset nIndex = rBox.GetSttIdx();
+ ::InsertCnt_( this, rBox.GetFrameFormat()->GetDoc(), ++nIndex );
+ }
+ else
+ {
+ const SwTableLines &rLines = rBox.GetTabLines();
+ SwFrame *pTmpPrev = nullptr;
+ for ( size_t i = 0; i < rLines.size(); ++i )
+ {
+ SwRowFrame *pNew = new SwRowFrame( *rLines[i], this, bInsertContent );
+ pNew->InsertBehind( this, pTmpPrev );
+ pTmpPrev = pNew;
+ }
+ }
+}
+
+void SwCellFrame::DestroyImpl()
+{
+ sw::BroadcastingModify* pMod = GetFormat();
+ if( pMod )
+ {
+ // At this stage the lower frames aren't destroyed already,
+ // therefore we have to do a recursive dispose.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ pRootFrame->GetCurrShell()->Imp()->DisposeAccessibleFrame( this, true );
+ }
+#endif
+
+ pMod->Remove( this );
+ if( !pMod->HasWriterListeners() )
+ delete pMod;
+ }
+
+ SwLayoutFrame::DestroyImpl();
+}
+
+SwCellFrame::~SwCellFrame()
+{
+}
+
+static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva )
+{
+ bool bRet = false;
+ SwFrame *pFrame = pLay->Lower();
+ SwRectFnSet aRectFnSet(pLay);
+ while ( pFrame )
+ {
+ tools::Long nFrameTop = aRectFnSet.GetTop(pFrame->getFrameArea());
+ if( nFrameTop != lYStart )
+ {
+ bRet = true;
+ const tools::Long lDiff = aRectFnSet.YDiff( lYStart, nFrameTop );
+ const tools::Long lDiffX = lYStart - nFrameTop;
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame);
+ aRectFnSet.SubTop( aFrm, -lDiff );
+ aRectFnSet.AddBottom( aFrm, lDiff );
+ }
+
+ pFrame->SetCompletePaint();
+
+ if ( !pFrame->GetNext() )
+ pFrame->SetRetouche();
+ if( bInva )
+ pFrame->Prepare( PrepareHint::FramePositionChanged );
+ if ( pFrame->IsLayoutFrame() && static_cast<SwLayoutFrame*>(pFrame)->Lower() )
+ lcl_ArrangeLowers( static_cast<SwLayoutFrame*>(pFrame),
+ aRectFnSet.GetTop(static_cast<SwLayoutFrame*>(pFrame)->Lower()->getFrameArea())
+ + lDiffX, bInva );
+ if ( pFrame->GetDrawObjs() )
+ {
+ for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
+ // #i26945# - check, if anchored object
+ // is lower of layout frame by checking, if the anchor
+ // frame, which contains the anchor position, is a lower
+ // of the layout frame.
+ if ( !pLay->IsAnLower( pAnchoredObj->GetAnchorFrameContainingAnchPos() ) )
+ {
+ continue;
+ }
+ // #i52904# - distinguish between anchored
+ // objects, whose vertical position depends on its anchor
+ // frame and whose vertical position is independent
+ // from its anchor frame.
+ bool bVertPosDepOnAnchor( true );
+ {
+ SwFormatVertOrient aVert( pAnchoredObj->GetFrameFormat().GetVertOrient() );
+ switch ( aVert.GetRelationOrient() )
+ {
+ case text::RelOrientation::PAGE_FRAME:
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ bVertPosDepOnAnchor = false;
+ break;
+ default: break;
+ }
+ }
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+
+ // OD 2004-05-18 #i28701# - no direct move of objects,
+ // which are anchored to-paragraph/to-character, if
+ // the wrapping style influence has to be considered
+ // on the object positioning.
+ // #i52904# - no direct move of objects,
+ // whose vertical position doesn't depend on anchor frame.
+ const bool bDirectMove =
+ FAR_AWAY != pFly->getFrameArea().Top() &&
+ bVertPosDepOnAnchor &&
+ !pFly->ConsiderObjWrapInfluenceOnObjPos();
+ if ( bDirectMove )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
+ aRectFnSet.SubTop( aFrm, -lDiff );
+ aRectFnSet.AddBottom( aFrm, lDiff );
+ }
+
+ pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
+ // --> OD 2004-08-17 - also notify view of <SdrObject>
+ // instance, which represents the Writer fly frame in
+ // the drawing layer
+ pFly->GetVirtDrawObj()->SetChanged();
+ // #i58280#
+ pFly->InvalidateObjRectWithSpaces();
+ }
+
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ static_cast<SwFlyInContentFrame*>(pFly)->AddRefOfst( lDiff );
+ // #115759# - reset current relative
+ // position to get re-positioned, if not directly moved.
+ if ( !bDirectMove )
+ {
+ pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
+ }
+ }
+ else if( pFly->IsAutoPos() )
+ {
+ pFly->AddLastCharY( lDiff );
+ // OD 2004-05-18 #i28701# - follow-up of #i22341#
+ // <mnLastTopOfLine> has also been adjusted.
+ pFly->AddLastTopOfLineY( lDiff );
+ }
+ // #i26945# - re-registration at
+ // page frame of anchor frame, if table frame isn't
+ // a follow table and table frame isn't in its
+ // rebuild of last line.
+ const SwTabFrame* pTabFrame = pLay->FindTabFrame();
+ // - save: check, if table frame is found.
+ if ( pTabFrame &&
+ !( pTabFrame->IsFollow() &&
+ pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
+ pFly->IsFlyFreeFrame() )
+ {
+ SwPageFrame* pPageFrame = pFly->GetPageFrame();
+ SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
+ if ( pPageFrame != pPageOfAnchor )
+ {
+ pFly->InvalidatePos();
+ if ( pPageFrame )
+ pPageFrame->MoveFly( pFly, pPageOfAnchor );
+ else
+ pPageOfAnchor->AppendFlyToPage( pFly );
+ }
+ }
+ // OD 2004-05-11 #i28701# - Because of the introduction
+ // of new positionings and alignments (e.g. aligned at
+ // page area, but anchored at-character), the position
+ // of the Writer fly frame has to be invalidated.
+ pFly->InvalidatePos();
+
+ // #i26945# - follow-up of #i3317#
+ // No arrangement of lowers, if Writer fly frame isn't
+ // moved
+ if ( bDirectMove &&
+ ::lcl_ArrangeLowers( pFly,
+ aRectFnSet.GetPrtTop(*pFly),
+ bInva ) )
+ {
+ pFly->SetCompletePaint();
+ }
+ }
+ else if ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchoredObj ) != nullptr )
+ {
+ // #i26945#
+ const SwTabFrame* pTabFrame = pLay->FindTabFrame();
+ if ( pTabFrame &&
+ !( pTabFrame->IsFollow() &&
+ pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
+ (pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ != RndStdIds::FLY_AS_CHAR))
+ {
+ SwPageFrame* pPageFrame = pAnchoredObj->GetPageFrame();
+ SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
+ if ( pPageFrame != pPageOfAnchor )
+ {
+ pAnchoredObj->InvalidateObjPos();
+ if ( pPageFrame )
+ {
+ pPageFrame->RemoveDrawObjFromPage( *pAnchoredObj );
+ }
+ pPageOfAnchor->AppendDrawObjToPage( *pAnchoredObj );
+ }
+ }
+ // #i28701# - adjust last character
+ // rectangle and last top of line.
+ pAnchoredObj->AddLastCharY( lDiff );
+ pAnchoredObj->AddLastTopOfLineY( lDiff );
+ // #i52904# - re-introduce direct move
+ // of drawing objects
+ const bool bDirectMove =
+ static_cast<const SwDrawFrameFormat&>(pAnchoredObj->GetFrameFormat()).IsPosAttrSet() &&
+ bVertPosDepOnAnchor &&
+ !pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos();
+ if ( bDirectMove )
+ {
+ SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
+ if ( aRectFnSet.IsVert() )
+ {
+ pAnchoredObj->DrawObj()->Move( Size( lDiff, 0 ) );
+ }
+ else
+ {
+ pAnchoredObj->DrawObj()->Move( Size( 0, lDiff ) );
+ }
+ // #i58280#
+ pAnchoredObj->InvalidateObjRectWithSpaces();
+ }
+ pAnchoredObj->InvalidateObjPos();
+ }
+ else
+ {
+ OSL_FAIL( "<lcl_ArrangeLowers(..)> - unknown type of anchored object!" );
+ }
+ }
+ }
+ }
+ // Columns and cells are ordered horizontal, not vertical
+ if( !pFrame->IsColumnFrame() && !pFrame->IsCellFrame() )
+ lYStart = aRectFnSet.YInc( lYStart,
+ aRectFnSet.GetHeight(pFrame->getFrameArea()) );
+
+ // Nowadays, the content inside a cell can flow into the follow table.
+ // Thus, the cell may only grow up to the end of the environment.
+ // So the content may have grown, but the cell could not grow.
+ // Therefore we have to trigger a formatting for the frames, which do
+ // not fit into the cell anymore:
+ SwTwips nDistanceToUpperPrtBottom =
+ aRectFnSet.BottomDist( pFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*pLay) );
+ // #i56146# - Revise fix of issue #i26945#
+ // do *not* consider content inside fly frames, if it's an undersized paragraph.
+ // #i26945# - consider content inside fly frames
+ if ( nDistanceToUpperPrtBottom < 0 &&
+ ( ( pFrame->IsInFly() &&
+ ( !pFrame->IsTextFrame() ||
+ !static_cast<SwTextFrame*>(pFrame)->IsUndersized() ) ) ||
+ pFrame->IsInSplitTableRow() ) )
+ {
+ pFrame->InvalidatePos();
+ }
+
+ pFrame = pFrame->GetNext();
+ }
+ return bRet;
+}
+
+void SwCellFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
+{
+ OSL_ENSURE( pAttrs, "CellFrame::Format, pAttrs is 0." );
+ const SwTabFrame* pTab = FindTabFrame();
+ SwRectFnSet aRectFnSet(pTab);
+
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+
+ //Adjust position.
+ if ( Lower() )
+ {
+ SwTwips nTopSpace, nBottomSpace, nLeftSpace, nRightSpace;
+ // #i29550#
+ if ( pTab->IsCollapsingBorders() && !Lower()->IsRowFrame() )
+ {
+ const SvxBoxItem& rBoxItem = pAttrs->GetBox();
+ nLeftSpace = rBoxItem.GetDistance( SvxBoxItemLine::LEFT );
+ nRightSpace = rBoxItem.GetDistance( SvxBoxItemLine::RIGHT );
+ nTopSpace = static_cast<SwRowFrame*>(GetUpper())->GetTopMarginForLowers();
+ nBottomSpace = static_cast<SwRowFrame*>(GetUpper())->GetBottomMarginForLowers();
+ }
+ else
+ {
+ // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
+ nLeftSpace = pAttrs->CalcLeft( this );
+ nRightSpace = pAttrs->CalcRight( this );
+ nTopSpace = pAttrs->CalcTop();
+ nBottomSpace = pAttrs->CalcBottom();
+ }
+ aRectFnSet.SetXMargins( *this, nLeftSpace, nRightSpace );
+ aRectFnSet.SetYMargins( *this, nTopSpace, nBottomSpace );
+ }
+ }
+ // #i26945#
+ tools::Long nRemaining = GetTabBox()->getRowSpan() >= 1 ?
+ ::lcl_CalcMinCellHeight( this, pTab->IsConsiderObjsForMinCellHeight(), pAttrs ) :
+ 0;
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFrameAreaSizeValid(true);
+
+ //The VarSize of the CellFrames is always the width.
+ //The width is not variable though, it is defined by the format.
+ //This predefined value however does not necessary match the actual
+ //width. The width is calculated based on the attribute, the value in
+ //the attribute matches the desired value of the TabFrame. Changes which
+ //were done there are taken into account here proportionately.
+ //If the cell doesn't have a neighbour anymore, it does not take the
+ //attribute into account and takes the rest of the upper instead.
+ SwTwips nWidth;
+ if ( GetNext() )
+ {
+ const SwTwips nWish = pTab->GetFormat()->GetFrameSize().GetWidth();
+ nWidth = pAttrs->GetSize().Width();
+
+ OSL_ENSURE( nWish, "Table without width?" );
+ OSL_ENSURE( nWidth <= nWish, "Width of cell larger than table." );
+ OSL_ENSURE( nWidth > 0, "Box without width" );
+
+ const tools::Long nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
+ if ( nWish != nPrtWidth )
+ {
+ // Avoid rounding problems, at least for the new table model
+ if ( pTab->GetTable()->IsNewModel() )
+ {
+ // 1. sum of widths of cells up to this cell (in model)
+ const SwTableLine* pTabLine = GetTabBox()->GetUpper();
+ const SwTableBoxes& rBoxes = pTabLine->GetTabBoxes();
+ const SwTableBox* pTmpBox = nullptr;
+
+ SwTwips nSumWidth = 0;
+ size_t i = 0;
+ do
+ {
+ pTmpBox = rBoxes[ i++ ];
+ nSumWidth += pTmpBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+ while ( pTmpBox != GetTabBox() );
+
+ // 2. calculate actual width of cells up to this one
+ double nTmpWidth = nSumWidth;
+ nTmpWidth *= nPrtWidth;
+ nTmpWidth /= nWish;
+ nWidth = static_cast<SwTwips>(nTmpWidth);
+
+ // 3. calculate frame widths of cells up to this one:
+ const SwFrame* pTmpCell = static_cast<const SwLayoutFrame*>(GetUpper())->Lower();
+ SwTwips nSumFrameWidths = 0;
+ while ( pTmpCell != this )
+ {
+ nSumFrameWidths += aRectFnSet.GetWidth(pTmpCell->getFrameArea());
+ pTmpCell = pTmpCell->GetNext();
+ }
+
+ nWidth = nWidth - nSumFrameWidths;
+ }
+ else
+ {
+ // #i12092# use double instead of long,
+ // otherwise this could lead to overflows
+ double nTmpWidth = nWidth;
+ nTmpWidth *= nPrtWidth;
+ nTmpWidth /= nWish;
+ nWidth = static_cast<SwTwips>(nTmpWidth);
+ }
+ }
+ }
+ else
+ {
+ OSL_ENSURE( pAttrs->GetSize().Width() > 0, "Box without width" );
+ nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
+ SwFrame *pPre = GetUpper()->Lower();
+ while ( pPre != this )
+ {
+ nWidth -= aRectFnSet.GetWidth(pPre->getFrameArea());
+ pPre = pPre->GetNext();
+ }
+ }
+
+ const tools::Long nDiff = nWidth - aRectFnSet.GetWidth(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if( IsNeighbourFrame() && IsRightToLeft() )
+ {
+ aRectFnSet.SubLeft( aFrm, nDiff );
+ }
+ else
+ {
+ aRectFnSet.AddRight( aFrm, nDiff );
+ }
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.AddRight( aPrt, nDiff );
+ }
+
+ //Adjust the height, it's defined through the content and the margins.
+ const tools::Long nDiffHeight = nRemaining - aRectFnSet.GetHeight(getFrameArea());
+ if ( nDiffHeight )
+ {
+ if ( nDiffHeight > 0 )
+ {
+ //Validate again if no growth happened. Invalidation is done
+ //through AdjustCells of the row.
+ if ( !Grow( nDiffHeight ) )
+ {
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ }
+ }
+ else
+ {
+ // Only keep invalidated if shrinking was actually done; the
+ // attempt can be ignored because all horizontally adjoined
+ // cells have to be the same height.
+ if ( !Shrink( -nDiffHeight ) )
+ {
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ }
+ }
+ }
+ }
+ const SwFormatVertOrient &rOri = pAttrs->GetAttrSet().GetVertOrient();
+
+ if ( !Lower() )
+ return;
+
+ // From now on, all operations are related to the table cell.
+ aRectFnSet.Refresh(this);
+
+ SwPageFrame* pPg = nullptr;
+ if ( !FindTabFrame()->IsRebuildLastLine() && text::VertOrientation::NONE != rOri.GetVertOrient() &&
+ // #158225# no vertical alignment of covered cells
+ !IsCoveredCell() &&
+ (pPg = FindPageFrame())!=nullptr )
+ {
+ if ( !Lower()->IsContentFrame() && !Lower()->IsSctFrame() && !Lower()->IsTabFrame() )
+ {
+ // OSL_ENSURE(for HTML-import!
+ OSL_ENSURE( false, "VAlign to cell without content" );
+ return;
+ }
+ bool bVertDir = true;
+ // #i43913# - no vertical alignment, if wrapping
+ // style influence is considered on object positioning and
+ // an object is anchored inside the cell.
+ const bool bConsiderWrapOnObjPos( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) );
+ // No alignment if fly with wrap overlaps the cell.
+ if ( pPg->GetSortedObjs() )
+ {
+ SwRect aRect( getFramePrintArea() ); aRect += getFrameArea().Pos();
+ for (SwAnchoredObject* pAnchoredObj : *pPg->GetSortedObjs())
+ {
+ SwRect aTmp( pAnchoredObj->GetObjRect() );
+ const SwFrame* pAnch = pAnchoredObj->GetAnchorFrame();
+ if ( (bConsiderWrapOnObjPos && IsAnLower( pAnch )) || (!bConsiderWrapOnObjPos && aTmp.Overlaps( aRect )) )
+ {
+ const SwFrameFormat& rAnchoredObjFrameFormat = pAnchoredObj->GetFrameFormat();
+ const SwFormatSurround &rSur = rAnchoredObjFrameFormat.GetSurround();
+
+ if ( bConsiderWrapOnObjPos || css::text::WrapTextMode_THROUGH != rSur.GetSurround() )
+ {
+ // frames, which the cell is a lower of, aren't relevant
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsAnLower( this ) )
+ continue;
+ }
+
+ // #i43913#
+ // #i52904# - no vertical alignment,
+ // if object, anchored inside cell, has temporarily
+ // consider its wrapping style on object positioning.
+ // #i58806# - no vertical alignment
+ // if object does not follow the text flow.
+ if ( bConsiderWrapOnObjPos ||
+ !IsAnLower( pAnch ) ||
+ pAnchoredObj->IsTmpConsiderWrapInfluence() ||
+ !rAnchoredObjFrameFormat.GetFollowTextFlow().GetValue() )
+ {
+ bVertDir = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ if( ( bVertDir && ( nRemaining -= lcl_CalcTopAndBottomMargin( *this, *pAttrs ) ) < nPrtHeight ) ||
+ aRectFnSet.GetTop(Lower()->getFrameArea()) != aRectFnSet.GetPrtTop(*this) )
+ {
+ tools::Long nDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nRemaining;
+ if ( nDiff >= 0 )
+ {
+ tools::Long lTopOfst = 0;
+ if ( bVertDir )
+ {
+ switch ( rOri.GetVertOrient() )
+ {
+ case text::VertOrientation::CENTER: lTopOfst = nDiff / 2; break;
+ case text::VertOrientation::BOTTOM: lTopOfst = nDiff; break;
+ default: break;
+ }
+ }
+ tools::Long nTmp = aRectFnSet.YInc(
+ aRectFnSet.GetPrtTop(*this), lTopOfst );
+ if ( lcl_ArrangeLowers( this, nTmp, !bVertDir ) )
+ SetCompletePaint();
+ }
+ }
+ }
+ else
+ {
+ //Was an old alignment taken into account?
+ if ( Lower()->IsContentFrame() )
+ {
+ const tools::Long lYStart = aRectFnSet.GetPrtTop(*this);
+ lcl_ArrangeLowers( this, lYStart, true );
+ }
+ }
+
+ // Handle rotated portions of lowers: it's possible that we have changed amount of vertical
+ // space since the last format, and this affects how many rotated portions we need. So throw
+ // away the current portions to build them using the new line width.
+ for (SwFrame* pFrame = Lower(); pFrame; pFrame = pFrame->GetNext())
+ {
+ if (!pFrame->IsTextFrame())
+ {
+ continue;
+ }
+
+ auto pTextFrame = static_cast<SwTextFrame*>(pFrame);
+ if (!pTextFrame->GetHasRotatedPortions())
+ {
+ continue;
+ }
+
+ pTextFrame->Prepare();
+ }
+}
+
+void SwCellFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if(auto pNewFormatHint = dynamic_cast<const sw::TableBoxFormatChanged*>(&rHint))
+ {
+ if(GetTabBox() != &pNewFormatHint->m_rTableBox)
+ return;
+ RegisterToFormat(const_cast<SwTableBoxFormat&>(pNewFormatHint->m_rNewFormat));
+ InvalidateSize();
+ InvalidatePrt_();
+ SetCompletePaint();
+ SetDerivedVert(false);
+ CheckDirChange();
+
+ // #i47489#
+ // make sure that the row will be formatted, in order
+ // to have the correct Get(Top|Bottom)MarginForLowers values
+ // set at the row.
+ const SwTabFrame* pTab = FindTabFrame();
+ if(pTab && pTab->IsCollapsingBorders())
+ {
+ SwFrame* pRow = GetUpper();
+ pRow->InvalidateSize_();
+ pRow->InvalidatePrt_();
+ }
+ }
+ else if(auto pMoveTableBoxHint = dynamic_cast<const sw::MoveTableBoxHint*>(&rHint))
+ {
+ if(GetTabBox() != &pMoveTableBoxHint->m_rTableBox)
+ return;
+ const_cast<SwFrameFormat*>(&pMoveTableBoxHint->m_rNewFormat)->Add(this);
+ InvalidateAll();
+ ReinitializeFrameSizeAttrFlags();
+ SetDerivedVert(false);
+ CheckDirChange();
+ return;
+ }
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const SfxPoolItem* pVertOrientItem = nullptr;
+ const SfxPoolItem* pProtectItem = nullptr;
+ const SfxPoolItem* pFrameDirItem = nullptr;
+ const SfxPoolItem* pBoxItem = nullptr;
+ const auto nWhich = pLegacy->m_pNew ? pLegacy->m_pNew->Which() : 0;
+ switch(nWhich)
+ {
+ case RES_ATTRSET_CHG:
+ {
+ auto& rChgSet = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
+ pVertOrientItem = rChgSet.GetItemIfSet(RES_VERT_ORIENT, false);
+ pProtectItem = rChgSet.GetItemIfSet(RES_PROTECT, false);
+ pFrameDirItem = rChgSet.GetItemIfSet(RES_FRAMEDIR, false);
+ pBoxItem = rChgSet.GetItemIfSet(RES_BOX, false);
+ break;
+ }
+ case RES_VERT_ORIENT:
+ pVertOrientItem = pLegacy->m_pNew;
+ break;
+ case RES_PROTECT:
+ pProtectItem = pLegacy->m_pNew;
+ break;
+ case RES_FRAMEDIR:
+ pFrameDirItem = pLegacy->m_pNew;
+ break;
+ case RES_BOX:
+ pBoxItem = pLegacy->m_pNew;
+ break;
+ }
+ if(pVertOrientItem)
+ {
+ bool bInva = true;
+ const auto eVertOrient = static_cast<const SwFormatVertOrient*>(pVertOrientItem)->GetVertOrient();
+ if(text::VertOrientation::NONE == eVertOrient && Lower() && Lower()->IsContentFrame())
+ {
+ SwRectFnSet aRectFnSet(this);
+ const tools::Long lYStart = aRectFnSet.GetPrtTop(*this);
+ bInva = lcl_ArrangeLowers(this, lYStart, false);
+ }
+ if (bInva)
+ {
+ SetCompletePaint();
+ InvalidatePrt();
+ }
+ }
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if(pProtectItem)
+ {
+ SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if(pSh && pSh->GetLayout()->IsAnyShellAccessible())
+ pSh->Imp()->InvalidateAccessibleEditableState(true, this);
+ }
+#endif
+ if(pFrameDirItem)
+ {
+ SetDerivedVert(false);
+ CheckDirChange();
+ }
+ // #i29550#
+ if(pBoxItem)
+ {
+ SwFrame* pTmpUpper = GetUpper();
+ while(pTmpUpper->GetUpper() && !pTmpUpper->GetUpper()->IsTabFrame())
+ pTmpUpper = pTmpUpper->GetUpper();
+
+ SwTabFrame* pTabFrame = static_cast<SwTabFrame*>(pTmpUpper->GetUpper());
+ if(pTabFrame->IsCollapsingBorders())
+ {
+ // Invalidate lowers of this and next row:
+ lcl_InvalidateAllLowersPrt(static_cast<SwRowFrame*>(pTmpUpper));
+ pTmpUpper = pTmpUpper->GetNext();
+ if(pTmpUpper)
+ lcl_InvalidateAllLowersPrt(static_cast<SwRowFrame*>(pTmpUpper));
+ else
+ pTabFrame->InvalidatePrt();
+ }
+ }
+ SwLayoutFrame::SwClientNotify(rMod, rHint);
+ }
+}
+
+tools::Long SwCellFrame::GetLayoutRowSpan() const
+{
+ const SwTableBox *pTabBox = GetTabBox();
+ tools::Long nRet = pTabBox ? pTabBox->getRowSpan() : 0;
+ if ( nRet < 1 )
+ {
+ const SwFrame* pRow = GetUpper();
+ const SwTabFrame* pTab = pRow ? static_cast<const SwTabFrame*>(pRow->GetUpper()) : nullptr;
+
+ if ( pTab && pTab->IsFollow() && pRow == pTab->GetFirstNonHeadlineRow() )
+ nRet = -nRet;
+ }
+ return nRet;
+}
+
+const SwCellFrame* SwCellFrame::GetCoveredCellInRow(const SwRowFrame& rRow) const
+{
+ if (GetLayoutRowSpan() <= 1)
+ {
+ // Not merged vertically.
+ return nullptr;
+ }
+
+ for (const SwFrame* pCell = rRow.GetLower(); pCell; pCell = pCell->GetNext())
+ {
+ if (!pCell->IsCellFrame())
+ {
+ continue;
+ }
+
+ auto pCellFrame = static_cast<const SwCellFrame*>(pCell);
+ if (!pCellFrame->IsCoveredCell())
+ {
+ continue;
+ }
+
+ if (pCellFrame->getFrameArea().Left() != getFrameArea().Left())
+ {
+ continue;
+ }
+
+ if (pCellFrame->getFrameArea().Width() != getFrameArea().Width())
+ {
+ continue;
+ }
+
+ // pCellFrame is covered, there are only covered cell frames between "this" and pCellFrame
+ // and the horizontal position/size matches "this".
+ return pCellFrame;
+ }
+
+ return nullptr;
+}
+
+std::vector<const SwCellFrame*> SwCellFrame::GetCoveredCells() const
+{
+ std::vector<const SwCellFrame*> aRet;
+ if (GetLayoutRowSpan() <= 1)
+ {
+ return aRet;
+ }
+
+ if (!GetUpper()->IsRowFrame())
+ {
+ return aRet;
+ }
+
+ auto pFirstRowFrame = static_cast<const SwRowFrame*>(GetUpper());
+ if (!pFirstRowFrame->GetNext())
+ {
+ return aRet;
+ }
+
+ if (!pFirstRowFrame->GetNext()->IsRowFrame())
+ {
+ return aRet;
+ }
+
+ for (const SwFrame* pRow = pFirstRowFrame->GetNext(); pRow; pRow = pRow->GetNext())
+ {
+ if (!pRow->IsRowFrame())
+ {
+ continue;
+ }
+
+ auto pRowFrame = static_cast<const SwRowFrame*>(pRow);
+ const SwCellFrame* pCovered = GetCoveredCellInRow(*pRowFrame);
+ if (!pCovered)
+ {
+ continue;
+ }
+
+ // Found a cell in a next row that is covered by "this".
+ aRet.push_back(pCovered);
+ }
+
+ return aRet;
+}
+
+void SwCellFrame::dumpAsXmlAttributes(xmlTextWriterPtr pWriter) const
+{
+ SwFrame::dumpAsXmlAttributes(pWriter);
+ if (SwCellFrame* pFollow = GetFollowCell())
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("follow"), "%" SAL_PRIuUINT32, pFollow->GetFrameId());
+
+ if (SwCellFrame* pPrevious = GetPreviousCell())
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("precede"), "%" SAL_PRIuUINT32, pPrevious->GetFrameId());
+}
+
+// #i103961#
+void SwCellFrame::Cut()
+{
+ // notification for accessibility
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
+ {
+ SwViewShell* pVSh = pRootFrame->GetCurrShell();
+ if ( pVSh && pVSh->Imp() )
+ {
+ pVSh->Imp()->DisposeAccessibleFrame( this );
+ }
+ }
+ }
+#endif
+
+ SwLayoutFrame::Cut();
+}
+
+// Helper functions for repeated headlines:
+
+bool SwTabFrame::IsInHeadline( const SwFrame& rFrame ) const
+{
+ OSL_ENSURE( IsAnLower( &rFrame ) && rFrame.IsInTab(),
+ "SwTabFrame::IsInHeadline called for frame not lower of table" );
+
+ const SwFrame* pTmp = &rFrame;
+ while ( !pTmp->GetUpper()->IsTabFrame() )
+ pTmp = pTmp->GetUpper();
+
+ return GetTable()->IsHeadline( *static_cast<const SwRowFrame*>(pTmp)->GetTabLine() );
+}
+
+/*
+ * If this is a master table, we can may assume, that there are at least
+ * nRepeat lines in the table.
+ * If this is a follow table, there are intermediate states for the table
+ * layout, e.g., during deletion of rows, which makes it necessary to find
+ * the first non-headline row by evaluating the headline flag at the row frame.
+ */
+SwRowFrame* SwTabFrame::GetFirstNonHeadlineRow() const
+{
+ SwRowFrame* pRet = const_cast<SwRowFrame*>(static_cast<const SwRowFrame*>(Lower()));
+ if ( pRet )
+ {
+ if ( IsFollow() )
+ {
+ while ( pRet && pRet->IsRepeatedHeadline() )
+ pRet = static_cast<SwRowFrame*>(pRet->GetNext());
+ }
+ else
+ {
+ sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
+ while ( pRet && nRepeat > 0 )
+ {
+ pRet = static_cast<SwRowFrame*>(pRet->GetNext());
+ --nRepeat;
+ }
+ }
+ }
+
+ return pRet;
+}
+
+bool SwTable::IsHeadline( const SwTableLine& rLine ) const
+{
+ for ( sal_uInt16 i = 0; i < GetRowsToRepeat(); ++i )
+ if ( GetTabLines()[ i ] == &rLine )
+ return true;
+
+ return false;
+}
+
+bool SwTabFrame::IsLayoutSplitAllowed() const
+{
+ return GetFormat()->GetLayoutSplit().GetValue();
+}
+
+// #i29550#
+
+sal_uInt16 SwTabFrame::GetBottomLineSize() const
+{
+ OSL_ENSURE( IsCollapsingBorders(),
+ "BottomLineSize only required for collapsing borders" );
+
+ OSL_ENSURE( Lower(), "Warning! Trying to prevent a crash" );
+
+ const SwFrame* pTmp = GetLastLower();
+
+ // #124755# Try to make code robust
+ if ( !pTmp ) return 0;
+
+ return static_cast<const SwRowFrame*>(pTmp)->GetBottomLineSize();
+}
+
+bool SwTabFrame::IsCollapsingBorders() const
+{
+ return GetFormat()->GetAttrSet().Get( RES_COLLAPSING_BORDERS ).GetValue();
+}
+
+/// Local helper function to calculate height of first text row
+static SwTwips lcl_CalcHeightOfFirstContentLine( const SwRowFrame& rSourceLine )
+{
+ // Find corresponding split line in master table
+ const SwTabFrame* pTab = rSourceLine.FindTabFrame();
+ SwRectFnSet aRectFnSet(pTab);
+ const SwCellFrame* pCurrSourceCell = static_cast<const SwCellFrame*>(rSourceLine.Lower());
+
+ // 1. Case: rSourceLine is a follow flow line.
+ // In this case we have to return the minimum of the heights
+ // of the first lines in rSourceLine.
+
+ // 2. Case: rSourceLine is not a follow flow line.
+ // In this case we have to return the maximum of the heights
+ // of the first lines in rSourceLine.
+ bool bIsInFollowFlowLine = rSourceLine.IsInFollowFlowRow();
+ SwTwips nHeight = bIsInFollowFlowLine ? LONG_MAX : 0;
+
+ while ( pCurrSourceCell )
+ {
+ // NEW TABLES
+ // Skip cells which are not responsible for the height of
+ // the follow flow line:
+ if ( bIsInFollowFlowLine && pCurrSourceCell->GetLayoutRowSpan() > 1 )
+ {
+ pCurrSourceCell = static_cast<const SwCellFrame*>(pCurrSourceCell->GetNext());
+ continue;
+ }
+
+ const SwFrame *pTmp = pCurrSourceCell->Lower();
+ if ( pTmp )
+ {
+ SwTwips nTmpHeight = USHRT_MAX;
+ // #i32456# Consider lower row frames
+ if ( pTmp->IsRowFrame() )
+ {
+ const SwRowFrame* pTmpSourceRow = static_cast<const SwRowFrame*>(pCurrSourceCell->Lower());
+ nTmpHeight = lcl_CalcHeightOfFirstContentLine( *pTmpSourceRow );
+ }
+ else if (pTmp->IsTabFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTabFrame()))
+ {
+ SwTabFrame const*const pTabFrame(pTmp->IsTabFrame()
+ ? static_cast<SwTabFrame const*>(pTmp)
+ : static_cast<SwTabFrame const*>(pTmp->GetLower()));
+ nTmpHeight = pTabFrame->CalcHeightOfFirstContentLine();
+ }
+ else if (pTmp->IsTextFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTextFrame()))
+ {
+ // Section frames don't influence the size/position of text
+ // frames, so 'text frame' and 'text frame in section frame' is
+ // the same case.
+ SwTextFrame* pTextFrame = nullptr;
+ if (pTmp->IsTextFrame())
+ pTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp));
+ else
+ pTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp->GetLower()));
+ pTextFrame->GetFormatted();
+ nTmpHeight = pTextFrame->FirstLineHeight();
+ }
+
+ if ( USHRT_MAX != nTmpHeight )
+ {
+ const SwCellFrame* pPrevCell = pCurrSourceCell->GetPreviousCell();
+ if ( pPrevCell )
+ {
+ // If we are in a split row, there may be some space
+ // left in the cell frame of the master row.
+ // We look for the minimum of all first line heights;
+ SwTwips nReal = aRectFnSet.GetHeight(pPrevCell->getFramePrintArea());
+ const SwFrame* pFrame = pPrevCell->Lower();
+ const SwFrame* pLast = pFrame;
+ while ( pFrame )
+ {
+ nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pLast = pFrame;
+ pFrame = pFrame->GetNext();
+ }
+
+ // #i26831#, #i26520#
+ // The additional lower space of the current last.
+ // #115759# - do *not* consider the
+ // additional lower space for 'master' text frames
+ if ( pLast && pLast->IsFlowFrame() &&
+ ( !pLast->IsTextFrame() ||
+ !static_cast<const SwTextFrame*>(pLast)->GetFollow() ) )
+ {
+ nReal += SwFlowFrame::CastFlowFrame(pLast)->CalcAddLowerSpaceAsLastInTableCell();
+ }
+ // Don't forget the upper space and lower space,
+ // #115759# - do *not* consider the upper
+ // and the lower space for follow text frames.
+ if ( pTmp->IsFlowFrame() &&
+ ( !pTmp->IsTextFrame() ||
+ !static_cast<const SwTextFrame*>(pTmp)->IsFollow() ) )
+ {
+ nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcUpperSpace( nullptr, pLast);
+ nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcLowerSpace();
+ }
+ // #115759# - consider additional lower
+ // space of <pTmp>, if contains only one line.
+ // In this case it would be the new last text frame, which
+ // would have no follow and thus would add this space.
+ if ( pTmp->IsTextFrame() &&
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp))
+ ->GetLineCount(TextFrameIndex(COMPLETE_STRING)) == 1)
+ {
+ nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)
+ ->CalcAddLowerSpaceAsLastInTableCell();
+ }
+ if ( nReal > 0 )
+ nTmpHeight -= nReal;
+ }
+ else
+ {
+ // pFirstRow is not a FollowFlowRow. In this case,
+ // we look for the maximum of all first line heights:
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCurrSourceCell );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ nTmpHeight += rAttrs.CalcTop() + rAttrs.CalcBottom();
+ // #i26250#
+ // Don't forget the upper space and lower space,
+ if ( pTmp->IsFlowFrame() )
+ {
+ nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcUpperSpace();
+ nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcLowerSpace();
+ }
+ }
+ }
+
+ if ( bIsInFollowFlowLine )
+ {
+ // minimum
+ if ( nTmpHeight < nHeight )
+ nHeight = nTmpHeight;
+ }
+ else
+ {
+ // maximum
+ if ( nTmpHeight > nHeight && USHRT_MAX != nTmpHeight )
+ nHeight = nTmpHeight;
+ }
+ }
+
+ pCurrSourceCell = static_cast<const SwCellFrame*>(pCurrSourceCell->GetNext());
+ }
+
+ return ( LONG_MAX == nHeight ) ? 0 : nHeight;
+}
+
+/// Function to calculate height of first text row
+SwTwips SwTabFrame::CalcHeightOfFirstContentLine() const
+{
+ SwRectFnSet aRectFnSet(this);
+
+ const bool bDontSplit = !IsFollow() && !GetFormat()->GetLayoutSplit().GetValue();
+
+ if ( bDontSplit )
+ {
+ // Table is not allowed to split: Take the whole height, that's all
+ return aRectFnSet.GetHeight(getFrameArea());
+ }
+
+ SwTwips nTmpHeight = 0;
+
+ const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
+ OSL_ENSURE( !IsFollow() || pFirstRow, "FollowTable without Lower" );
+
+ // NEW TABLES
+ if ( pFirstRow && pFirstRow->IsRowSpanLine() && pFirstRow->GetNext() )
+ pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext());
+
+ // Calculate the height of the headlines:
+ const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
+ SwTwips nRepeatHeight = nRepeat ? lcl_GetHeightOfRows( GetLower(), nRepeat ) : 0;
+
+ // Calculate the height of the keeping lines
+ // (headlines + following keeping lines):
+ SwTwips nKeepHeight = nRepeatHeight;
+ if ( GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) )
+ {
+ sal_uInt16 nKeepRows = nRepeat;
+
+ // Check how many rows want to keep together
+ while ( pFirstRow && pFirstRow->ShouldRowKeepWithNext() )
+ {
+ ++nKeepRows;
+ pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext());
+ }
+
+ if ( nKeepRows > nRepeat )
+ nKeepHeight = lcl_GetHeightOfRows( GetLower(), nKeepRows );
+ }
+
+ // For master tables, the height of the headlines + the height of the
+ // keeping lines (if any) has to be considered. For follow tables, we
+ // only consider the height of the keeping rows without the repeated lines:
+ if ( !IsFollow() )
+ {
+ nTmpHeight = nKeepHeight;
+ }
+ else
+ {
+ nTmpHeight = nKeepHeight - nRepeatHeight;
+ }
+
+ // pFirstRow row is the first non-heading row.
+ // nTmpHeight is the height of the heading row if we are a follow.
+ if ( pFirstRow )
+ {
+ const bool bSplittable = pFirstRow->IsRowSplitAllowed();
+ const SwTwips nFirstLineHeight = aRectFnSet.GetHeight(pFirstRow->getFrameArea());
+
+ if ( !bSplittable )
+ {
+ // pFirstRow is not splittable, but it is still possible that the line height of pFirstRow
+ // actually is determined by a lower cell with rowspan = -1. In this case we should not
+ // just return the height of the first line. Basically we need to get the height of the
+ // line as it would be on the last page. Since this is quite complicated to calculate,
+ // we only calculate the height of the first line.
+ SwFormatFrameSize const& rFrameSize(pFirstRow->GetAttrSet()->GetFrameSize());
+ if ( pFirstRow->GetPrev() &&
+ static_cast<const SwRowFrame*>(pFirstRow->GetPrev())->IsRowSpanLine()
+ && rFrameSize.GetHeightSizeType() != SwFrameSize::Fixed)
+ {
+ // Calculate maximum height of all cells with rowspan = 1:
+ SwTwips nMaxHeight = rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum
+ ? rFrameSize.GetHeight()
+ : 0;
+ const SwCellFrame* pLower2 = static_cast<const SwCellFrame*>(pFirstRow->Lower());
+ while ( pLower2 )
+ {
+ if ( 1 == pLower2->GetTabBox()->getRowSpan() )
+ {
+ const SwTwips nCellHeight = lcl_CalcMinCellHeight( pLower2, true );
+ nMaxHeight = std::max( nCellHeight, nMaxHeight );
+ }
+ pLower2 = static_cast<const SwCellFrame*>(pLower2->GetNext());
+ }
+ nTmpHeight += nMaxHeight;
+ }
+ else
+ {
+ nTmpHeight += nFirstLineHeight;
+ }
+ }
+
+ // Optimization: lcl_CalcHeightOfFirstContentLine actually can trigger
+ // a formatting of the row frame (via the GetFormatted()). We don't
+ // want this formatting if the row does not have a height.
+ else if ( 0 != nFirstLineHeight )
+ {
+ const bool bOldJoinLock = IsJoinLocked();
+ const_cast<SwTabFrame*>(this)->LockJoin();
+ const SwTwips nHeightOfFirstContentLine = lcl_CalcHeightOfFirstContentLine( *pFirstRow );
+
+ // Consider minimum row height:
+ const SwFormatFrameSize &rSz = pFirstRow->GetFormat()->GetFrameSize();
+
+ SwTwips nMinRowHeight = 0;
+ if (rSz.GetHeightSizeType() == SwFrameSize::Minimum)
+ {
+ nMinRowHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pFirstRow),
+ tools::Long(0));
+ }
+
+ nTmpHeight += std::max( nHeightOfFirstContentLine, nMinRowHeight );
+
+ if ( !bOldJoinLock )
+ const_cast<SwTabFrame*>(this)->UnlockJoin();
+ }
+ }
+
+ return nTmpHeight;
+}
+
+// Some more functions for covered/covering cells. This way inclusion of
+// SwCellFrame can be avoided
+
+bool SwFrame::IsLeaveUpperAllowed() const
+{
+ return false;
+}
+
+bool SwCellFrame::IsLeaveUpperAllowed() const
+{
+ return GetLayoutRowSpan() > 1;
+}
+
+bool SwFrame::IsCoveredCell() const
+{
+ return false;
+}
+
+bool SwCellFrame::IsCoveredCell() const
+{
+ return GetLayoutRowSpan() < 1;
+}
+
+bool SwFrame::IsInCoveredCell() const
+{
+ bool bRet = false;
+
+ const SwFrame* pThis = this;
+ while ( pThis && !pThis->IsCellFrame() )
+ pThis = pThis->GetUpper();
+
+ if ( pThis )
+ bRet = pThis->IsCoveredCell();
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/trvlfrm.cxx b/sw/source/core/layout/trvlfrm.cxx
new file mode 100644
index 000000000..375b9fe34
--- /dev/null
+++ b/sw/source/core/layout/trvlfrm.cxx
@@ -0,0 +1,2654 @@
+/* -*- 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 <hints.hxx>
+#include <comphelper/flagguard.hxx>
+#include <tools/line.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/protitem.hxx>
+#include <vcl/settings.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtsrnd.hxx>
+#include <pagedesc.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <ftnfrm.hxx>
+#include <flyfrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <viewopt.hxx>
+#include <DocumentSettingManager.hxx>
+#include <viscrs.hxx>
+#include <dflyobj.hxx>
+#include <crstate.hxx>
+#include <dcontact.hxx>
+#include <sortedobjs.hxx>
+#include <txatbase.hxx>
+#include <fmtfld.hxx>
+#include <fldbas.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <ndtxt.hxx>
+#include <undobj.hxx>
+
+#include <swselectionlist.hxx>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+namespace {
+ bool lcl_GetModelPositionForViewPoint_Objects( const SwPageFrame* pPageFrame, bool bSearchBackground,
+ SwPosition *pPos, Point const & rPoint, SwCursorMoveState* pCMS )
+ {
+ bool bRet = false;
+ Point aPoint( rPoint );
+ SwOrderIter aIter( pPageFrame );
+ aIter.Top();
+ while ( aIter() )
+ {
+ const SwVirtFlyDrawObj* pObj =
+ static_cast<const SwVirtFlyDrawObj*>(aIter());
+ const SwAnchoredObject* pAnchoredObj = GetUserCall( aIter() )->GetAnchoredObj( aIter() );
+ const SwFormatSurround& rSurround = pAnchoredObj->GetFrameFormat().GetSurround();
+ const SvxOpaqueItem& rOpaque = pAnchoredObj->GetFrameFormat().GetOpaque();
+ bool bInBackground = ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) && !rOpaque.GetValue();
+
+ bool bBackgroundMatches = bInBackground == bSearchBackground;
+
+ const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr;
+ if ( pFly && bBackgroundMatches &&
+ ( ( pCMS && pCMS->m_bSetInReadOnly ) ||
+ !pFly->IsProtected() ) &&
+ pFly->GetModelPositionForViewPoint( pPos, aPoint, pCMS ) )
+ {
+ bRet = true;
+ break;
+ }
+
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ aIter.Prev();
+ }
+ return bRet;
+ }
+
+ double lcl_getDistance( const SwRect& rRect, const Point& rPoint )
+ {
+ double nDist = 0.0;
+
+ // If the point is inside the rectangle, then distance is 0
+ // Otherwise, compute the distance to the center of the rectangle.
+ if ( !rRect.Contains( rPoint ) )
+ {
+ tools::Line aLine( rPoint, rRect.Center( ) );
+ nDist = aLine.GetLength( );
+ }
+
+ return nDist;
+ }
+}
+
+namespace {
+
+//For SwFlyFrame::GetModelPositionForViewPoint
+class SwCursorOszControl
+{
+public:
+ // So the compiler can initialize the class already. No DTOR and member
+ // as public members
+ const SwFlyFrame* m_pEntry;
+ const SwFlyFrame* m_pStack1;
+ const SwFlyFrame* m_pStack2;
+
+ bool ChkOsz( const SwFlyFrame *pFly )
+ {
+ bool bRet = true;
+ if (pFly != m_pStack1 && pFly != m_pStack2)
+ {
+ m_pStack1 = m_pStack2;
+ m_pStack2 = pFly;
+ bRet = false;
+ }
+ return bRet;
+ }
+
+ void Entry( const SwFlyFrame *pFly )
+ {
+ if (!m_pEntry)
+ m_pEntry = m_pStack1 = pFly;
+ }
+
+ void Exit( const SwFlyFrame *pFly )
+ {
+ if (pFly == m_pEntry)
+ m_pEntry = m_pStack1 = m_pStack2 = nullptr;
+ }
+};
+
+}
+
+static SwCursorOszControl g_OszCtrl = { nullptr, nullptr, nullptr };
+
+/** Searches the ContentFrame owning the PrtArea containing the point. */
+bool SwLayoutFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
+ SwCursorMoveState* pCMS, bool ) const
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ bool bRet = false;
+ const SwFrame *pFrame = Lower();
+ while ( !bRet && pFrame )
+ {
+ pFrame->Calc(pRenderContext);
+
+ // #i43742# New function
+ const bool bContentCheck = pFrame->IsTextFrame() && pCMS && pCMS->m_bContentCheck;
+ const SwRect aPaintRect( bContentCheck ?
+ pFrame->UnionFrame() :
+ pFrame->GetPaintArea() );
+
+ if ( aPaintRect.Contains( rPoint ) &&
+ ( bContentCheck || pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS ) ) )
+ bRet = true;
+ else
+ pFrame = pFrame->GetNext();
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ }
+ return bRet;
+}
+
+/** Searches the page containing the searched point. */
+
+bool SwPageFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
+ SwCursorMoveState* pCMS, bool bTestBackground ) const
+{
+ Point aPoint( rPoint );
+
+ // check, if we have to adjust the point
+ if ( !getFrameArea().Contains( aPoint ) )
+ {
+ aPoint.setX( std::max( aPoint.X(), getFrameArea().Left() ) );
+ aPoint.setX( std::min( aPoint.X(), getFrameArea().Right() ) );
+ aPoint.setY( std::max( aPoint.Y(), getFrameArea().Top() ) );
+ aPoint.setY( std::min( aPoint.Y(), getFrameArea().Bottom() ) );
+ }
+
+ bool bRet = false;
+ //Could it be a free flying one?
+ //If his content should be protected, we can't set the Cursor in it, thus
+ //all changes should be impossible.
+ if ( GetSortedObjs() )
+ {
+ bRet = lcl_GetModelPositionForViewPoint_Objects( this, false, pPos, rPoint, pCMS );
+ }
+
+ if ( !bRet )
+ {
+ SwPosition aBackPos( *pPos );
+ SwPosition aTextPos( *pPos );
+
+ //We fix the StartPoint if no Content below the page 'answers' and then
+ //start all over again one page before the current one.
+ //However we can't use Flys in such a case.
+ if (!SwLayoutFrame::GetModelPositionForViewPoint(&aTextPos, aPoint, pCMS))
+ {
+ if ( pCMS && (pCMS->m_bStop || pCMS->m_bExactOnly) )
+ {
+ pCMS->m_bStop = true;
+ return false;
+ }
+
+ const SwContentFrame *pCnt = GetContentPos( aPoint, false, false, pCMS, false );
+ // GetContentPos may have modified pCMS
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+
+ bool bTextRet = false;
+
+ OSL_ENSURE( pCnt, "Cursor is gone to a Black hole" );
+ if( pCMS && pCMS->m_pFill && pCnt->IsTextFrame() )
+ bTextRet = pCnt->GetModelPositionForViewPoint( &aTextPos, rPoint, pCMS );
+ else
+ bTextRet = pCnt->GetModelPositionForViewPoint( &aTextPos, aPoint, pCMS );
+
+ if ( !bTextRet )
+ {
+ // Set point to pCnt, delete mark
+ // this may happen, if pCnt is hidden
+ if (pCnt->IsTextFrame())
+ {
+ aTextPos = static_cast<SwTextFrame const*>(pCnt)->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ assert(pCnt->IsNoTextFrame());
+ aTextPos = SwPosition( *static_cast<SwNoTextFrame const*>(pCnt)->GetNode() );
+ }
+ }
+ }
+
+ SwContentNode* pContentNode = aTextPos.nNode.GetNode().GetContentNode();
+ bool bConsiderBackground = true;
+ // If the text position is a clickable field, then that should have priority.
+ if (pContentNode && pContentNode->IsTextNode())
+ {
+ SwTextNode* pTextNd = pContentNode->GetTextNode();
+ SwTextAttr* pTextAttr = pTextNd->GetTextAttrForCharAt(aTextPos.nContent.GetIndex(), RES_TXTATR_FIELD);
+ if (pTextAttr)
+ {
+ const SwField* pField = pTextAttr->GetFormatField().GetField();
+ if (pField->IsClickable())
+ bConsiderBackground = false;
+ }
+ }
+
+ bool bBackRet = false;
+ // Check objects in the background if nothing else matched
+ if ( GetSortedObjs() )
+ {
+ bBackRet = lcl_GetModelPositionForViewPoint_Objects( this, true, &aBackPos, rPoint, pCMS );
+ }
+
+ if (bConsiderBackground && bTestBackground && bBackRet)
+ {
+ (*pPos) = aBackPos;
+ }
+ else if (!bBackRet)
+ {
+ (*pPos) = aTextPos;
+ }
+ else // bBackRet && !(bConsiderBackground && bTestBackground)
+ {
+ /* In order to provide a selection as accurate as possible when we have both
+ * text and background object, then we compute the distance between both
+ * would-be positions and the click point. The shortest distance wins.
+ */
+ double nTextDistance = 0;
+ bool bValidTextDistance = false;
+ if (pContentNode)
+ {
+ SwContentFrame* pTextFrame = pContentNode->getLayoutFrame( getRootFrame( ) );
+
+ // try this again but prefer the "previous" position
+ SwCursorMoveState aMoveState;
+ SwCursorMoveState *const pState(pCMS ? pCMS : &aMoveState);
+ comphelper::FlagRestorationGuard g(
+ pState->m_bPosMatchesBounds, true);
+ SwPosition prevTextPos(*pPos);
+ if (SwLayoutFrame::GetModelPositionForViewPoint(&prevTextPos, aPoint, pState))
+ {
+ SwRect aTextRect;
+ pTextFrame->GetCharRect(aTextRect, prevTextPos);
+
+ if (prevTextPos.nContent < pContentNode->Len())
+ {
+ // aRextRect is just a line on the left edge of the
+ // previous character; to get a better measure from
+ // lcl_getDistance, extend that to a rectangle over
+ // the entire character.
+ SwPosition const nextTextPos(prevTextPos.nNode,
+ SwIndex(prevTextPos.nContent, +1));
+ SwRect nextTextRect;
+ pTextFrame->GetCharRect(nextTextRect, nextTextPos);
+ SwRectFnSet aRectFnSet(pTextFrame);
+ if (aRectFnSet.GetTop(aTextRect) ==
+ aRectFnSet.GetTop(nextTextRect)) // same line?
+ {
+ // need to handle mixed RTL/LTR portions somehow
+ if (aRectFnSet.GetLeft(aTextRect) <
+ aRectFnSet.GetLeft(nextTextRect))
+ {
+ aRectFnSet.SetRight( aTextRect,
+ aRectFnSet.GetLeft(nextTextRect));
+ }
+ else // RTL
+ {
+ aRectFnSet.SetLeft( aTextRect,
+ aRectFnSet.GetLeft(nextTextRect));
+ }
+ }
+ }
+
+ nTextDistance = lcl_getDistance(aTextRect, rPoint);
+ bValidTextDistance = true;
+ }
+ }
+
+ double nBackDistance = 0;
+ bool bValidBackDistance = false;
+ SwContentNode* pBackNd = aBackPos.nNode.GetNode( ).GetContentNode( );
+ if ( pBackNd && bConsiderBackground)
+ {
+ // FIXME There are still cases were we don't have the proper node here.
+ SwContentFrame* pBackFrame = pBackNd->getLayoutFrame( getRootFrame( ) );
+ if (pBackFrame)
+ {
+ SwRect rBackRect;
+ pBackFrame->GetCharRect( rBackRect, aBackPos );
+
+ nBackDistance = lcl_getDistance( rBackRect, rPoint );
+ bValidBackDistance = true;
+ }
+ }
+
+ if ( bValidTextDistance && bValidBackDistance && basegfx::fTools::more( nTextDistance, nBackDistance ) )
+ {
+ (*pPos) = aBackPos;
+ }
+ else
+ {
+ (*pPos) = aTextPos;
+ }
+ }
+ }
+
+ rPoint = aPoint;
+ return true;
+}
+
+bool SwLayoutFrame::FillSelection( SwSelectionList& rList, const SwRect& rRect ) const
+{
+ if( rRect.Overlaps(GetPaintArea()) )
+ {
+ const SwFrame* pFrame = Lower();
+ while( pFrame )
+ {
+ pFrame->FillSelection( rList, rRect );
+ pFrame = pFrame->GetNext();
+ }
+ }
+ return false;
+}
+
+bool SwPageFrame::FillSelection( SwSelectionList& rList, const SwRect& rRect ) const
+{
+ bool bRet = false;
+ if( rRect.Overlaps(GetPaintArea()) )
+ {
+ bRet = SwLayoutFrame::FillSelection( rList, rRect );
+ if( GetSortedObjs() )
+ {
+ const SwSortedObjs &rObjs = *GetSortedObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
+ if( !pFly )
+ continue;
+ if( pFly->FillSelection( rList, rRect ) )
+ bRet = true;
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SwRootFrame::FillSelection( SwSelectionList& aSelList, const SwRect& rRect) const
+{
+ const SwFrame *pPage = Lower();
+ const tools::Long nBottom = rRect.Bottom();
+ while( pPage )
+ {
+ if( pPage->getFrameArea().Top() < nBottom )
+ {
+ if( pPage->getFrameArea().Bottom() > rRect.Top() )
+ pPage->FillSelection( aSelList, rRect );
+ pPage = pPage->GetNext();
+ }
+ else
+ pPage = nullptr;
+ }
+ return !aSelList.isEmpty();
+}
+
+/** Primary passes the call to the first page.
+ *
+ * @return false, if the passed Point gets changed
+ */
+bool SwRootFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
+ SwCursorMoveState* pCMS, bool bTestBackground ) const
+{
+ const bool bOldAction = IsCallbackActionEnabled();
+ const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
+ OSL_ENSURE( (Lower() && Lower()->IsPageFrame()), "No PageFrame found." );
+ if( pCMS && pCMS->m_pFill )
+ pCMS->m_bFillRet = false;
+ Point aOldPoint = rPoint;
+
+ // search for page containing rPoint. The borders around the pages are considered
+ const SwPageFrame* pPage = GetPageAtPos( rPoint, nullptr, true );
+
+ // #i95626#
+ // special handling for <rPoint> beyond root frames area
+ if ( !pPage &&
+ rPoint.X() > getFrameArea().Right() &&
+ rPoint.Y() > getFrameArea().Bottom() )
+ {
+ pPage = dynamic_cast<const SwPageFrame*>(Lower());
+ while ( pPage && pPage->GetNext() )
+ {
+ pPage = dynamic_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+ }
+ if ( pPage )
+ {
+ pPage->SwPageFrame::GetModelPositionForViewPoint( pPos, rPoint, pCMS, bTestBackground );
+ }
+
+ const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
+ if( pCMS )
+ {
+ if( pCMS->m_bStop )
+ return false;
+ if( pCMS->m_pFill )
+ return pCMS->m_bFillRet;
+ }
+ return aOldPoint == rPoint;
+}
+
+/**
+ * If this is about a Content-carrying cell the Cursor will be force inserted into one of the ContentFrames
+ * if there are no other options.
+ *
+ * There is no entry for protected cells.
+ */
+bool SwCellFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
+ SwCursorMoveState* pCMS, bool ) const
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ // cell frame does not necessarily have a lower (split table cell)
+ if ( !Lower() )
+ return false;
+
+ if ( !(pCMS && pCMS->m_bSetInReadOnly) &&
+ GetFormat()->GetProtect().IsContentProtected() )
+ return false;
+
+ if ( pCMS && pCMS->m_eState == CursorMoveState::TableSel )
+ {
+ const SwTabFrame *pTab = FindTabFrame();
+ if ( pTab->IsFollow() && pTab->IsInHeadline( *this ) )
+ {
+ pCMS->m_bStop = true;
+ return false;
+ }
+ }
+
+ if ( Lower() )
+ {
+ if ( Lower()->IsLayoutFrame() )
+ return SwLayoutFrame::GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ else
+ {
+ Calc(pRenderContext);
+ bool bRet = false;
+
+ const SwFrame *pFrame = Lower();
+ while ( pFrame && !bRet )
+ {
+ pFrame->Calc(pRenderContext);
+ if ( pFrame->getFrameArea().Contains( rPoint ) )
+ {
+ bRet = pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ }
+ pFrame = pFrame->GetNext();
+ }
+ if ( !bRet )
+ {
+ const bool bFill = pCMS && pCMS->m_pFill;
+ Point aPoint( rPoint );
+ const SwContentFrame *pCnt = GetContentPos( rPoint, true );
+ if( bFill && pCnt->IsTextFrame() )
+ {
+ rPoint = aPoint;
+ }
+ pCnt->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//Problem: If two Flys have the same size and share the same position then
+//they end inside each other.
+//Because we recursively check if a Point doesn't randomly lie inside another
+//fly which lies completely inside the current Fly we could trigger an endless
+//loop with the mentioned situation above.
+//Using the helper class SwCursorOszControl we prevent the recursion. During
+//a recursion GetModelPositionForViewPoint picks the one which lies on top.
+bool SwFlyFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
+ SwCursorMoveState* pCMS, bool ) const
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ g_OszCtrl.Entry( this );
+
+ //If the Points lies inside the Fly, we try hard to set the Cursor inside it.
+ //However if the Point sits inside a Fly which is completely located inside
+ //the current one, we call GetModelPositionForViewPoint for it.
+ Calc(pRenderContext);
+ bool bInside = getFrameArea().Contains( rPoint ) && Lower();
+ bool bRet = false;
+
+ //If a Frame contains a graphic, but only text was requested, it basically
+ //won't accept the Cursor.
+ if ( bInside && pCMS && pCMS->m_eState == CursorMoveState::SetOnlyText &&
+ (!Lower() || Lower()->IsNoTextFrame()) )
+ bInside = false;
+
+ const SwPageFrame *pPage = FindPageFrame();
+ if ( bInside && pPage && pPage->GetSortedObjs() )
+ {
+ SwOrderIter aIter( pPage );
+ aIter.Top();
+ while ( aIter() && !bRet )
+ {
+ const SwVirtFlyDrawObj* pObj = static_cast<const SwVirtFlyDrawObj*>(aIter());
+ const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr;
+ if ( pFly && pFly->getFrameArea().Contains( rPoint ) &&
+ getFrameArea().Contains( pFly->getFrameArea() ) )
+ {
+ if (g_OszCtrl.ChkOsz(pFly))
+ break;
+ bRet = pFly->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ if ( bRet )
+ break;
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ }
+ aIter.Next();
+ }
+ }
+
+ while ( bInside && !bRet )
+ {
+ const SwFrame *pFrame = Lower();
+ while ( pFrame && !bRet )
+ {
+ pFrame->Calc(pRenderContext);
+ if ( pFrame->getFrameArea().Contains( rPoint ) )
+ {
+ bRet = pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ }
+ pFrame = pFrame->GetNext();
+ }
+ if ( !bRet )
+ {
+ const bool bFill = pCMS && pCMS->m_pFill;
+ Point aPoint( rPoint );
+ const SwContentFrame *pCnt = GetContentPos( rPoint, true, false, pCMS );
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ if( bFill && pCnt->IsTextFrame() )
+ {
+ rPoint = aPoint;
+ }
+ pCnt->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ bRet = true;
+ }
+ }
+ g_OszCtrl.Exit( this );
+ return bRet;
+}
+
+/** Layout dependent cursor travelling */
+bool SwNoTextFrame::LeftMargin(SwPaM *pPam) const
+{
+ if( &pPam->GetNode() != GetNode() )
+ return false;
+ const_cast<SwContentNode*>(GetNode())->
+ MakeStartIndex(&pPam->GetPoint()->nContent);
+ return true;
+}
+
+bool SwNoTextFrame::RightMargin(SwPaM *pPam, bool) const
+{
+ if( &pPam->GetNode() != GetNode() )
+ return false;
+ const_cast<SwContentNode*>(GetNode())->
+ MakeEndIndex(&pPam->GetPoint()->nContent);
+ return true;
+}
+
+static const SwContentFrame *lcl_GetNxtCnt( const SwContentFrame* pCnt )
+{
+ return pCnt->GetNextContentFrame();
+}
+
+static const SwContentFrame *lcl_GetPrvCnt( const SwContentFrame* pCnt )
+{
+ return pCnt->GetPrevContentFrame();
+}
+
+typedef const SwContentFrame *(*GetNxtPrvCnt)( const SwContentFrame* );
+
+/// Frame in repeated headline?
+static bool lcl_IsInRepeatedHeadline( const SwFrame *pFrame,
+ const SwTabFrame** ppTFrame = nullptr )
+{
+ const SwTabFrame *pTab = pFrame->FindTabFrame();
+ if( ppTFrame )
+ *ppTFrame = pTab;
+ return pTab && pTab->IsFollow() && pTab->IsInHeadline( *pFrame );
+}
+
+/// Skip protected table cells. Optionally also skip repeated headlines.
+//MA 1998-01-26: Chg also skip other protected areas
+//FME: Skip follow flow cells
+static const SwContentFrame * lcl_MissProtectedFrames( const SwContentFrame *pCnt,
+ GetNxtPrvCnt fnNxtPrv,
+ bool bMissHeadline,
+ bool bInReadOnly,
+ bool bMissFollowFlowLine )
+{
+ if ( pCnt && pCnt->IsInTab() )
+ {
+ bool bProtect = true;
+ while ( pCnt && bProtect )
+ {
+ const SwLayoutFrame *pCell = pCnt->GetUpper();
+ while ( pCell && !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+ if ( !pCell ||
+ ( ( bInReadOnly || !pCell->GetFormat()->GetProtect().IsContentProtected() ) &&
+ ( !bMissHeadline || !lcl_IsInRepeatedHeadline( pCell ) ) &&
+ ( !bMissFollowFlowLine || !pCell->IsInFollowFlowRow() ) &&
+ !pCell->IsCoveredCell() ) )
+ bProtect = false;
+ else
+ pCnt = (*fnNxtPrv)( pCnt );
+ }
+ }
+ else if ( !bInReadOnly )
+ while ( pCnt && pCnt->IsProtected() )
+ pCnt = (*fnNxtPrv)( pCnt );
+
+ return pCnt;
+}
+
+static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart,
+ GetNxtPrvCnt fnNxtPrv, bool bInReadOnly )
+{
+ OSL_ENSURE( FrameContainsNode(*pStart, pPam->GetNode().GetIndex()),
+ "lcl_UpDown doesn't work for others." );
+
+ const SwContentFrame *pCnt = nullptr;
+
+ //We have to cheat a little bit during a table selection: Go to the
+ //beginning of the cell while going up and go to the end of the cell while
+ //going down.
+ bool bTableSel = false;
+ if ( pStart->IsInTab() &&
+ pPam->GetNode().StartOfSectionNode() !=
+ pPam->GetNode( false ).StartOfSectionNode() )
+ {
+ bTableSel = true;
+ const SwLayoutFrame *pCell = pStart->GetUpper();
+ while ( !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+
+ // Check, if cell has a Prev/Follow cell:
+ const bool bFwd = ( fnNxtPrv == lcl_GetNxtCnt );
+ const SwLayoutFrame* pTmpCell = bFwd ?
+ static_cast<const SwCellFrame*>(pCell)->GetFollowCell() :
+ static_cast<const SwCellFrame*>(pCell)->GetPreviousCell();
+
+ const SwContentFrame* pTmpStart = pStart;
+ while ( pTmpCell && nullptr != ( pTmpStart = pTmpCell->ContainsContent() ) )
+ {
+ pCell = pTmpCell;
+ pTmpCell = bFwd ?
+ static_cast<const SwCellFrame*>(pCell)->GetFollowCell() :
+ static_cast<const SwCellFrame*>(pCell)->GetPreviousCell();
+ }
+ const SwContentFrame *pNxt = pCnt = pTmpStart;
+
+ while ( pCell->IsAnLower( pNxt ) )
+ {
+ pCnt = pNxt;
+ pNxt = (*fnNxtPrv)( pNxt );
+ }
+ }
+
+ pCnt = (*fnNxtPrv)( pCnt ? pCnt : pStart );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+
+ const SwTabFrame *pStTab = pStart->FindTabFrame();
+ const SwTabFrame *pTable = nullptr;
+ const bool bTab = pStTab || (pCnt && pCnt->IsInTab());
+ bool bEnd = !bTab;
+
+ const SwFrame* pVertRefFrame = pStart;
+ if ( bTableSel && pStTab )
+ pVertRefFrame = pStTab;
+ SwRectFnSet aRectFnSet(pVertRefFrame);
+
+ SwTwips nX = 0;
+ if ( bTab )
+ {
+ // pStart or pCnt is inside a table. nX will be used for travelling:
+ SwRect aRect( pStart->getFrameArea() );
+ pStart->GetCharRect( aRect, *pPam->GetPoint() );
+ Point aCenter = aRect.Center();
+ nX = aRectFnSet.IsVert() ? aCenter.Y() : aCenter.X();
+
+ pTable = pCnt ? pCnt->FindTabFrame() : nullptr;
+ if ( !pTable )
+ pTable = pStTab;
+
+ if ( pStTab &&
+ !pStTab->GetUpper()->IsInTab() &&
+ !pTable->GetUpper()->IsInTab() )
+ {
+ const SwFrame *pCell = pStart->GetUpper();
+ while ( pCell && !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+ OSL_ENSURE( pCell, "could not find the cell" );
+ nX = aRectFnSet.XInc(aRectFnSet.GetLeft(pCell->getFrameArea()),
+ aRectFnSet.GetWidth(pCell->getFrameArea()) / 2);
+
+ //The flow leads from one table to the next. The X-value needs to be
+ //corrected based on the middle of the starting cell by the amount
+ //of the offset of the tables.
+ if ( pStTab != pTable )
+ {
+ nX += aRectFnSet.GetLeft(pTable->getFrameArea()) -
+ aRectFnSet.GetLeft(pStTab->getFrameArea());
+ }
+ }
+
+ // Restrict nX to the left and right borders of pTab:
+ // (is this really necessary?)
+ if (pTable && !pTable->GetUpper()->IsInTab())
+ {
+ const bool bRTL = pTable->IsRightToLeft();
+ const tools::Long nPrtLeft = bRTL ?
+ aRectFnSet.GetPrtRight(*pTable) :
+ aRectFnSet.GetPrtLeft(*pTable);
+ if (bRTL != (aRectFnSet.XDiff(nPrtLeft, nX) > 0))
+ nX = nPrtLeft;
+ else
+ {
+ const tools::Long nPrtRight = bRTL ?
+ aRectFnSet.GetPrtLeft(*pTable) :
+ aRectFnSet.GetPrtRight(*pTable);
+ if (bRTL != (aRectFnSet.XDiff(nX, nPrtRight) > 0))
+ nX = nPrtRight;
+ }
+ }
+ }
+
+ do
+ {
+ //If I'm in the DocumentBody, I want to stay there.
+ if ( pStart->IsInDocBody() )
+ {
+ while ( pCnt && (!pCnt->IsInDocBody() ||
+ (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow())))
+ {
+ pCnt = (*fnNxtPrv)( pCnt );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+ }
+ }
+
+ //If I'm in the FootNoteArea, I try to reach the next FootNoteArea in
+ //case of necessity.
+ else if ( pStart->IsInFootnote() )
+ {
+ while ( pCnt && (!pCnt->IsInFootnote() ||
+ (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow())))
+ {
+ pCnt = (*fnNxtPrv)( pCnt );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+ }
+ }
+
+ //In Flys we can go ahead blindly as long as we find a Content.
+ else if ( pStart->IsInFly() )
+ {
+ if ( pCnt && pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow() )
+ {
+ pCnt = (*fnNxtPrv)( pCnt );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+ }
+ }
+
+ //Otherwise I'll just refuse to leave to current area.
+ else if ( pCnt )
+ {
+ const SwFrame *pUp = pStart->GetUpper();
+ while (pUp && pUp->GetUpper() && !(pUp->GetType() & FRM_HEADFOOT))
+ pUp = pUp->GetUpper();
+ bool bSame = false;
+ const SwFrame *pCntUp = pCnt->GetUpper();
+ while ( pCntUp && !bSame )
+ {
+ if ( pUp == pCntUp )
+ bSame = true;
+ else
+ pCntUp = pCntUp->GetUpper();
+ }
+ if ( !bSame )
+ pCnt = nullptr;
+ else if (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()) // i73332
+ {
+ pCnt = (*fnNxtPrv)( pCnt );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+ }
+ }
+
+ if ( bTab )
+ {
+ if ( !pCnt )
+ bEnd = true;
+ else
+ {
+ const SwTabFrame *pTab = pCnt->FindTabFrame();
+ if( !pTab )
+ bEnd = true;
+ else
+ {
+ if ( pTab != pTable )
+ {
+ //The flow leads from one table to the next. The X-value
+ //needs to be corrected by the amount of the offset of
+ //the tables
+ if ( pTable &&
+ !pTab->GetUpper()->IsInTab() &&
+ !pTable->GetUpper()->IsInTab() )
+ nX += pTab->getFrameArea().Left() - pTable->getFrameArea().Left();
+ pTable = pTab;
+ }
+ const SwLayoutFrame *pCell = pCnt->GetUpper();
+ while ( pCell && !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+
+ Point aInsideCell;
+ Point aInsideCnt;
+ if ( pCell )
+ {
+ tools::Long nTmpTop = aRectFnSet.GetTop(pCell->getFrameArea());
+ if ( aRectFnSet.IsVert() )
+ {
+ if ( nTmpTop )
+ nTmpTop = aRectFnSet.XInc(nTmpTop, -1);
+
+ aInsideCell = Point( nTmpTop, nX );
+ }
+ else
+ aInsideCell = Point( nX, nTmpTop );
+ }
+
+ tools::Long nTmpTop = aRectFnSet.GetTop(pCnt->getFrameArea());
+ if ( aRectFnSet.IsVert() )
+ {
+ if ( nTmpTop )
+ nTmpTop = aRectFnSet.XInc(nTmpTop, -1);
+
+ aInsideCnt = Point( nTmpTop, nX );
+ }
+ else
+ aInsideCnt = Point( nX, nTmpTop );
+
+ if ( pCell && pCell->getFrameArea().Contains( aInsideCell ) )
+ {
+ bEnd = true;
+ //Get the right Content out of the cell.
+ if ( !pCnt->getFrameArea().Contains( aInsideCnt ) )
+ {
+ pCnt = pCell->ContainsContent();
+ if ( fnNxtPrv == lcl_GetPrvCnt )
+ while ( pCell->IsAnLower(pCnt->GetNextContentFrame()) )
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ }
+ else if ( pCnt->getFrameArea().Contains( aInsideCnt ) )
+ bEnd = true;
+ }
+ }
+ if ( !bEnd )
+ {
+ pCnt = (*fnNxtPrv)( pCnt );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+ }
+ }
+
+ } while ( !bEnd ||
+ (pCnt && pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()));
+
+ if (pCnt == nullptr)
+ {
+ return false;
+ }
+ if (pCnt->IsTextFrame())
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pCnt));
+ *pPam->GetPoint() = pFrame->MapViewToModelPos(TextFrameIndex(
+ fnNxtPrv == lcl_GetPrvCnt
+ ? pFrame->GetText().getLength()
+ : 0));
+ }
+ else
+ { // set the Point on the Content-Node
+ assert(pCnt->IsNoTextFrame());
+ SwContentNode *const pCNd = const_cast<SwContentNode*>(static_cast<SwNoTextFrame const*>(pCnt)->GetNode());
+ pPam->GetPoint()->nNode = *pCNd;
+ if ( fnNxtPrv == lcl_GetPrvCnt )
+ pCNd->MakeEndIndex( &pPam->GetPoint()->nContent );
+ else
+ pCNd->MakeStartIndex( &pPam->GetPoint()->nContent );
+ }
+ return true;
+}
+
+bool SwContentFrame::UnitUp( SwPaM* pPam, const SwTwips, bool bInReadOnly ) const
+{
+ return ::lcl_UpDown( pPam, this, lcl_GetPrvCnt, bInReadOnly );
+}
+
+bool SwContentFrame::UnitDown( SwPaM* pPam, const SwTwips, bool bInReadOnly ) const
+{
+ return ::lcl_UpDown( pPam, this, lcl_GetNxtCnt, bInReadOnly );
+}
+
+/** Returns the number of the current page.
+ *
+ * If the method gets a PaM then the current page is the one in which the PaM sits. Otherwise the
+ * current page is the first one inside the VisibleArea. We only work on available pages!
+ */
+sal_uInt16 SwRootFrame::GetCurrPage( const SwPaM *pActualCursor ) const
+{
+ OSL_ENSURE( pActualCursor, "got no page cursor" );
+ SwFrame const*const pActFrame = pActualCursor->GetPoint()->nNode.GetNode().
+ GetContentNode()->getLayoutFrame(this,
+ pActualCursor->GetPoint());
+ return pActFrame->FindPageFrame()->GetPhyPageNum();
+}
+
+/** Returns a PaM which sits at the beginning of the requested page.
+ *
+ * Formatting is done as far as necessary.
+ * The PaM sits on the last page, if the page number was chosen too big.
+ *
+ * @return Null, if the operation was not possible.
+ */
+sal_uInt16 SwRootFrame::SetCurrPage( SwCursor* pToSet, sal_uInt16 nPageNum )
+{
+ vcl::RenderContext* pRenderContext = GetCurrShell() ? GetCurrShell()->GetOut() : nullptr;
+ OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "No page available." );
+
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
+ bool bEnd =false;
+ while ( !bEnd && pPage->GetPhyPageNum() != nPageNum )
+ { if ( pPage->GetNext() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ else
+ { //Search the first ContentFrame and format until a new page is started
+ //or until the ContentFrame are all done.
+ const SwContentFrame *pContent = pPage->ContainsContent();
+ while ( pContent && pPage->IsAnLower( pContent ) )
+ {
+ pContent->Calc(pRenderContext);
+ pContent = pContent->GetNextContentFrame();
+ }
+ //Either this is a new page or we found the last page.
+ if ( pPage->GetNext() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ else
+ bEnd = true;
+ }
+ }
+ //pPage now points to the 'requested' page. Now we have to create the PaM
+ //on the beginning of the first ContentFrame in the body-text.
+ //If this is a footnote-page, the PaM will be set in the first footnote.
+ const SwContentFrame *pContent = pPage->ContainsContent();
+ if ( pPage->IsFootnotePage() )
+ while ( pContent && !pContent->IsInFootnote() )
+ pContent = pContent->GetNextContentFrame();
+ else
+ while ( pContent && !pContent->IsInDocBody() )
+ pContent = pContent->GetNextContentFrame();
+ if ( pContent )
+ {
+ assert(pContent->IsTextFrame());
+ SwTextFrame const*const pFrame(static_cast<const SwTextFrame*>(pContent));
+ *pToSet->GetPoint() = pFrame->MapViewToModelPos(pFrame->GetOffset());
+
+ SwShellCursor* pSCursor = dynamic_cast<SwShellCursor*>(pToSet);
+ if( pSCursor )
+ {
+ Point &rPt = pSCursor->GetPtPos();
+ rPt = pContent->getFrameArea().Pos();
+ rPt += pContent->getFramePrintArea().Pos();
+ }
+ return pPage->GetPhyPageNum();
+ }
+ return 0;
+}
+
+SwContentFrame *GetFirstSub( const SwLayoutFrame *pLayout )
+{
+ return const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pLayout))->FindFirstBodyContent();
+}
+
+SwContentFrame *GetLastSub( const SwLayoutFrame *pLayout )
+{
+ return const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pLayout))->FindLastBodyContent();
+}
+
+SwLayoutFrame *GetNextFrame( const SwLayoutFrame *pFrame )
+{
+ SwLayoutFrame *pNext =
+ (pFrame->GetNext() && pFrame->GetNext()->IsLayoutFrame()) ?
+ const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFrame->GetNext())) : nullptr;
+ // #i39402# in case of an empty page
+ if(pNext && !pNext->ContainsContent())
+ pNext = (pNext->GetNext() && pNext->GetNext()->IsLayoutFrame()) ?
+ static_cast<SwLayoutFrame*>(pNext->GetNext()) : nullptr;
+ return pNext;
+}
+
+SwLayoutFrame *GetThisFrame( const SwLayoutFrame *pFrame )
+{
+ return const_cast<SwLayoutFrame*>(pFrame);
+}
+
+SwLayoutFrame *GetPrevFrame( const SwLayoutFrame *pFrame )
+{
+ SwLayoutFrame *pPrev =
+ (pFrame->GetPrev() && pFrame->GetPrev()->IsLayoutFrame()) ?
+ const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFrame->GetPrev())) : nullptr;
+ // #i39402# in case of an empty page
+ if(pPrev && !pPrev->ContainsContent())
+ pPrev = (pPrev->GetPrev() && pPrev->GetPrev()->IsLayoutFrame()) ?
+ static_cast<SwLayoutFrame*>(pPrev->GetPrev()) : nullptr;
+ return pPrev;
+}
+
+/**
+ * Returns the first/last Contentframe (controlled using the parameter fnPosPage)
+ * of the current/previous/next page (controlled using the parameter fnWhichPage).
+ */
+bool GetFrameInPage( const SwContentFrame *pCnt, SwWhichPage fnWhichPage,
+ SwPosPage fnPosPage, SwPaM *pPam )
+{
+ //First find the requested page, at first the current, then the one which
+ //was requests through fnWichPage.
+ const SwLayoutFrame *pLayoutFrame = pCnt->FindPageFrame();
+ if ( !pLayoutFrame || (nullptr == (pLayoutFrame = (*fnWhichPage)(pLayoutFrame))) )
+ return false;
+
+ //Now the desired ContentFrame below the page
+ pCnt = (*fnPosPage)(pLayoutFrame);
+ if( nullptr == pCnt )
+ return false;
+ else
+ {
+ // repeated headlines in tables
+ if ( pCnt->IsInTab() && fnPosPage == GetFirstSub )
+ {
+ const SwTabFrame* pTab = pCnt->FindTabFrame();
+ if ( pTab->IsFollow() )
+ {
+ if ( pTab->IsInHeadline( *pCnt ) )
+ {
+ SwLayoutFrame* pRow = pTab->GetFirstNonHeadlineRow();
+ if ( pRow )
+ {
+ // We are in the first line of a follow table
+ // with repeated headings.
+ // To actually make a "real" move we take the first content
+ // of the next row
+ pCnt = pRow->ContainsContent();
+ if ( ! pCnt )
+ return false;
+ }
+ }
+ }
+ }
+
+ assert(pCnt->IsTextFrame());
+ SwTextFrame const*const pFrame(static_cast<const SwTextFrame*>(pCnt));
+ TextFrameIndex const nIdx((fnPosPage == GetFirstSub)
+ ? pFrame->GetOffset()
+ : (pFrame->GetFollow())
+ ? pFrame->GetFollow()->GetOffset() - TextFrameIndex(1)
+ : TextFrameIndex(pFrame->GetText().getLength()));
+ *pPam->GetPoint() = pFrame->MapViewToModelPos(nIdx);
+ return true;
+ }
+}
+
+static sal_uInt64 CalcDiff(const Point &rPt1, const Point &rPt2)
+{
+ //Calculate the distance between the two points.
+ //'delta' X^2 + 'delta'Y^2 = 'distance'^2
+ sal_uInt64 dX = std::max( rPt1.X(), rPt2.X() ) -
+ std::min( rPt1.X(), rPt2.X() ),
+ dY = std::max( rPt1.Y(), rPt2.Y() ) -
+ std::min( rPt1.Y(), rPt2.Y() );
+ return (dX * dX) + (dY * dY);
+}
+
+/** Check if the point lies inside the page part in which also the ContentFrame lies.
+ *
+ * In this context header, page body, footer and footnote-container count as page part.
+ * This will suit the purpose that the ContentFrame which lies in the "right" page part will be
+ * accepted instead of one which doesn't lie there although his distance to the point is shorter.
+ */
+static const SwLayoutFrame* lcl_Inside( const SwContentFrame *pCnt, Point const & rPt )
+{
+ const SwLayoutFrame* pUp = pCnt->GetUpper();
+ while( pUp )
+ {
+ if( pUp->IsPageBodyFrame() || pUp->IsFooterFrame() || pUp->IsHeaderFrame() )
+ {
+ if( rPt.Y() >= pUp->getFrameArea().Top() && rPt.Y() <= pUp->getFrameArea().Bottom() )
+ return pUp;
+ return nullptr;
+ }
+ if( pUp->IsFootnoteContFrame() )
+ return pUp->getFrameArea().Contains( rPt ) ? pUp : nullptr;
+ pUp = pUp->GetUpper();
+ }
+ return nullptr;
+}
+
+/** Search for the nearest Content to pass.
+ *
+ * Considers the previous, the current and the next page.
+ * If no content is found, the area gets expanded until one is found.
+ *
+ * @return The 'semantically correct' position inside the PrtArea of the found ContentFrame.
+ */
+const SwContentFrame *SwLayoutFrame::GetContentPos( Point& rPoint,
+ const bool bDontLeave,
+ const bool bBodyOnly,
+ SwCursorMoveState *pCMS,
+ const bool bDefaultExpand ) const
+{
+ //Determine the first ContentFrame.
+ const SwLayoutFrame *pStart = (!bDontLeave && bDefaultExpand && GetPrev()) ?
+ static_cast<const SwLayoutFrame*>(GetPrev()) : this;
+ const SwContentFrame *pContent = pStart->ContainsContent();
+
+ if ( !pContent && (GetPrev() && !bDontLeave) )
+ pContent = ContainsContent();
+
+ if ( bBodyOnly && pContent && !pContent->IsInDocBody() )
+ while ( pContent && !pContent->IsInDocBody() )
+ pContent = pContent->GetNextContentFrame();
+
+ const SwContentFrame *pActual= pContent;
+ const SwLayoutFrame *pInside = nullptr;
+ sal_uInt16 nMaxPage = GetPhyPageNum() + (bDefaultExpand ? 1 : 0);
+ Point aPoint = rPoint;
+ sal_uInt64 nDistance = SAL_MAX_UINT64;
+
+ while ( true ) //A loop to be sure we always find one.
+ {
+ while ( pContent &&
+ ((!bDontLeave || IsAnLower( pContent )) &&
+ (pContent->GetPhyPageNum() <= nMaxPage)) )
+ {
+ if ( pContent->getFrameArea().Width() &&
+ ( !bBodyOnly || pContent->IsInDocBody() ) )
+ {
+ //If the Content lies in a protected area (cell, Footnote, section),
+ //we search the next Content which is not protected.
+ const SwContentFrame *pComp = pContent;
+ pContent = ::lcl_MissProtectedFrames( pContent, lcl_GetNxtCnt, false,
+ pCMS && pCMS->m_bSetInReadOnly, false );
+ if ( pComp != pContent )
+ continue;
+
+ if ( !pContent->IsTextFrame() || !static_cast<const SwTextFrame*>(pContent)->IsHiddenNow() )
+ {
+ SwRect aContentFrame( pContent->UnionFrame() );
+ if ( aContentFrame.Contains( rPoint ) )
+ {
+ pActual = pContent;
+ aPoint = rPoint;
+ break;
+ }
+ //The distance from rPoint to the nearest Point of pContent
+ //will now be calculated.
+ Point aContentPoint( rPoint );
+
+ //First set the vertical position
+ if ( aContentFrame.Top() > aContentPoint.Y() )
+ aContentPoint.setY( aContentFrame.Top() );
+ else if ( aContentFrame.Bottom() < aContentPoint.Y() )
+ aContentPoint.setY( aContentFrame.Bottom() );
+
+ //Now the horizontal position
+ if ( aContentFrame.Left() > aContentPoint.X() )
+ aContentPoint.setX( aContentFrame.Left() );
+ else if ( aContentFrame.Right() < aContentPoint.X() )
+ aContentPoint.setX( aContentFrame.Right() );
+
+ // pInside is a page area in which the point lies. As soon
+ // as pInside != 0 only frames are accepted which are
+ // placed inside.
+ if( !pInside || ( pInside->IsAnLower( pContent ) &&
+ ( !pContent->IsInFootnote() || pInside->IsFootnoteContFrame() ) ) )
+ {
+ const sal_uInt64 nDiff = ::CalcDiff(aContentPoint, rPoint);
+ bool bBetter = nDiff < nDistance; // This one is nearer
+ if( !pInside )
+ {
+ pInside = lcl_Inside( pContent, rPoint );
+ if( pInside ) // In the "right" page area
+ bBetter = true;
+ }
+ if( bBetter )
+ {
+ aPoint = aContentPoint;
+ nDistance = nDiff;
+ pActual = pContent;
+ }
+ }
+ }
+ }
+ pContent = pContent->GetNextContentFrame();
+ if ( bBodyOnly )
+ while ( pContent && !pContent->IsInDocBody() )
+ pContent = pContent->GetNextContentFrame();
+ }
+ if ( !pActual )
+ { //If we not yet found one we have to expand the searched
+ //area, sometime we will find one!
+ //MA 1997-01-09: Opt for many empty pages - if we only search inside
+ //the body, we can expand the searched area sufficiently in one step.
+ if ( bBodyOnly )
+ {
+ while ( !pContent && pStart->GetPrev() )
+ {
+ ++nMaxPage;
+ if( !pStart->GetPrev()->IsLayoutFrame() )
+ return nullptr;
+ pStart = static_cast<const SwLayoutFrame*>(pStart->GetPrev());
+ if( pStart->IsInDocBody() )
+ pContent = pStart->ContainsContent();
+ else
+ {
+ const SwPageFrame *pPage = pStart->FindPageFrame();
+ if( !pPage )
+ return nullptr;
+ pContent = pPage->FindFirstBodyContent();
+ }
+ }
+ if ( !pContent ) // Somewhere down the road we have to start with one!
+ {
+ const SwPageFrame *pPage = pStart->FindPageFrame();
+ if( !pPage )
+ return nullptr;
+ pContent = pPage->GetUpper()->ContainsContent();
+ while ( pContent && !pContent->IsInDocBody() )
+ pContent = pContent->GetNextContentFrame();
+ if ( !pContent )
+ return nullptr; // There is no document content yet!
+ }
+ }
+ else
+ {
+ ++nMaxPage;
+ if ( pStart->GetPrev() )
+ {
+ if( !pStart->GetPrev()->IsLayoutFrame() )
+ return nullptr;
+ pStart = static_cast<const SwLayoutFrame*>(pStart->GetPrev());
+ pContent = pStart->ContainsContent();
+ }
+ else // Somewhere down the road we have to start with one!
+ {
+ const SwPageFrame *pPage = pStart->FindPageFrame();
+ if( !pPage )
+ return nullptr;
+ pContent = pPage->GetUpper()->ContainsContent();
+ }
+ }
+ pActual = pContent;
+ }
+ else
+ break;
+ }
+
+ OSL_ENSURE( pActual, "no Content found." );
+ OSL_ENSURE( !bBodyOnly || pActual->IsInDocBody(), "Content not in Body." );
+
+ //Special case for selecting tables not in repeated TableHeadlines.
+ if ( pActual->IsInTab() && pCMS && pCMS->m_eState == CursorMoveState::TableSel )
+ {
+ const SwTabFrame *pTab = pActual->FindTabFrame();
+ if ( pTab->IsFollow() && pTab->IsInHeadline( *pActual ) )
+ {
+ pCMS->m_bStop = true;
+ return nullptr;
+ }
+ }
+
+ //A small correction at the first/last
+ Size aActualSize( pActual->getFramePrintArea().SSize() );
+ if ( aActualSize.Height() > pActual->GetUpper()->getFramePrintArea().Height() )
+ aActualSize.setHeight( pActual->GetUpper()->getFramePrintArea().Height() );
+
+ SwRectFnSet aRectFnSet(pActual);
+ if ( !pActual->GetPrev() &&
+ aRectFnSet.YDiff( aRectFnSet.GetPrtTop(*pActual),
+ aRectFnSet.IsVert() ? rPoint.X() : rPoint.Y() ) > 0 )
+ {
+ aPoint.setY( pActual->getFrameArea().Top() + pActual->getFramePrintArea().Top() );
+ aPoint.setX( pActual->getFrameArea().Left() +
+ ( pActual->IsRightToLeft() || aRectFnSet.IsVert() ?
+ pActual->getFramePrintArea().Right() :
+ pActual->getFramePrintArea().Left() ) );
+ }
+ else if ( !pActual->GetNext() &&
+ aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pActual),
+ aRectFnSet.IsVert() ? rPoint.X() : rPoint.Y() ) < 0 )
+ {
+ aPoint.setY( pActual->getFrameArea().Top() + pActual->getFramePrintArea().Bottom() );
+ aPoint.setX( pActual->getFrameArea().Left() +
+ ( pActual->IsRightToLeft() || aRectFnSet.IsVert() ?
+ pActual->getFramePrintArea().Left() :
+ pActual->getFramePrintArea().Right() ) );
+ }
+
+ //Bring the Point into the PrtArea
+ const SwRect aRect( pActual->getFrameArea().Pos() + pActual->getFramePrintArea().Pos(),
+ aActualSize );
+ if ( aPoint.Y() < aRect.Top() )
+ aPoint.setY( aRect.Top() );
+ else if ( aPoint.Y() > aRect.Bottom() )
+ aPoint.setY( aRect.Bottom() );
+ if ( aPoint.X() < aRect.Left() )
+ aPoint.setX( aRect.Left() );
+ else if ( aPoint.X() > aRect.Right() )
+ aPoint.setX( aRect.Right() );
+ rPoint = aPoint;
+ return pActual;
+}
+
+/** Same as SwLayoutFrame::GetContentPos(). Specialized for fields and border. */
+void SwPageFrame::GetContentPosition( const Point &rPt, SwPosition &rPos ) const
+{
+ //Determine the first ContentFrame.
+ const SwContentFrame *pContent = ContainsContent();
+ if ( pContent )
+ {
+ //Look back one more (if possible).
+ const SwContentFrame *pTmp = pContent->GetPrevContentFrame();
+ while ( pTmp && !pTmp->IsInDocBody() )
+ pTmp = pTmp->GetPrevContentFrame();
+ if ( pTmp )
+ pContent = pTmp;
+ }
+ else
+ pContent = GetUpper()->ContainsContent();
+
+ const SwContentFrame *pAct = pContent;
+ Point aAct = rPt;
+ sal_uInt64 nDist = SAL_MAX_UINT64;
+
+ while ( pContent )
+ {
+ SwRect aContentFrame( pContent->UnionFrame() );
+ if ( aContentFrame.Contains( rPt ) )
+ {
+ //This is the nearest one.
+ pAct = pContent;
+ break;
+ }
+
+ //Calculate the distance from rPt to the nearest point of pContent.
+ Point aPoint( rPt );
+
+ //Calculate the vertical position first
+ if ( aContentFrame.Top() > rPt.Y() )
+ aPoint.setY( aContentFrame.Top() );
+ else if ( aContentFrame.Bottom() < rPt.Y() )
+ aPoint.setY( aContentFrame.Bottom() );
+
+ //And now the horizontal position
+ if ( aContentFrame.Left() > rPt.X() )
+ aPoint.setX( aContentFrame.Left() );
+ else if ( aContentFrame.Right() < rPt.X() )
+ aPoint.setX( aContentFrame.Right() );
+
+ const sal_uInt64 nDiff = ::CalcDiff( aPoint, rPt );
+ if ( nDiff < nDist )
+ {
+ aAct = aPoint;
+ nDist = nDiff;
+ pAct = pContent;
+ }
+ else if ( aContentFrame.Top() > getFrameArea().Bottom() )
+ //In terms of fields, it's not possible to be closer any more!
+ break;
+
+ pContent = pContent->GetNextContentFrame();
+ while ( pContent && !pContent->IsInDocBody() )
+ pContent = pContent->GetNextContentFrame();
+ }
+
+ //Bring the point into the PrtArea.
+ const SwRect aRect( pAct->getFrameArea().Pos() + pAct->getFramePrintArea().Pos(), pAct->getFramePrintArea().SSize() );
+ if ( aAct.Y() < aRect.Top() )
+ aAct.setY( aRect.Top() );
+ else if ( aAct.Y() > aRect.Bottom() )
+ aAct.setY( aRect.Bottom() );
+ if ( aAct.X() < aRect.Left() )
+ aAct.setX( aRect.Left() );
+ else if ( aAct.X() > aRect.Right() )
+ aAct.setX( aRect.Right() );
+
+ if (!pAct->isFrameAreaDefinitionValid() ||
+ (pAct->IsTextFrame() && !static_cast<SwTextFrame const*>(pAct)->HasPara()))
+ {
+ // ContentFrame not formatted -> always on node-beginning
+ // tdf#100635 also if the SwTextFrame would require reformatting,
+ // which is unwanted in case this is called from text formatting code
+ rPos = static_cast<SwTextFrame const*>(pAct)->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ SwCursorMoveState aTmpState( CursorMoveState::SetOnlyText );
+ pAct->GetModelPositionForViewPoint( &rPos, aAct, &aTmpState );
+ }
+}
+
+/** Search the nearest Content to the passed point.
+ *
+ * Only search inside the BodyText.
+ * @note Only the nearest vertically one will be searched.
+ * @note JP 11.10.2001: only in tables we try to find the right column - Bug 72294
+ */
+Point SwRootFrame::GetNextPrevContentPos( const Point& rPoint, bool bNext ) const
+{
+ vcl::RenderContext* pRenderContext = GetCurrShell() ? GetCurrShell()->GetOut() : nullptr;
+ // #123110# - disable creation of an action by a callback
+ // event during processing of this method. Needed because formatting is
+ // triggered by this method.
+ DisableCallbackAction aDisableCallbackAction(const_cast<SwRootFrame&>(*this));
+ //Search the first ContentFrame and his successor in the body area.
+ //To be efficient (and not formatting too much) we'll start at the correct
+ //page.
+ const SwLayoutFrame *pPage = static_cast<const SwLayoutFrame*>(Lower());
+ if( pPage )
+ while( pPage->GetNext() && pPage->getFrameArea().Bottom() < rPoint.Y() )
+ pPage = static_cast<const SwLayoutFrame*>(pPage->GetNext());
+
+ const SwContentFrame *pCnt = pPage ? pPage->ContainsContent() : ContainsContent();
+ while ( pCnt && !pCnt->IsInDocBody() )
+ pCnt = pCnt->GetNextContentFrame();
+
+ if ( !pCnt )
+ return Point( 0, 0 );
+
+ pCnt->Calc(pRenderContext);
+ if( !bNext )
+ {
+ // As long as the point lies before the first ContentFrame and there are
+ // still precedent pages I'll go to the next page.
+ while ( rPoint.Y() < pCnt->getFrameArea().Top() && pPage->GetPrev() )
+ {
+ pPage = static_cast<const SwLayoutFrame*>(pPage->GetPrev());
+ pCnt = pPage->ContainsContent();
+ while ( !pCnt )
+ {
+ pPage = static_cast<const SwLayoutFrame*>(pPage->GetPrev());
+ if ( pPage )
+ pCnt = pPage->ContainsContent();
+ else
+ return ContainsContent()->UnionFrame().Pos();
+ }
+ pCnt->Calc(pRenderContext);
+ }
+ }
+
+ //Does the point lie above the first ContentFrame?
+ if ( rPoint.Y() < pCnt->getFrameArea().Top() && !lcl_IsInRepeatedHeadline( pCnt ) )
+ return pCnt->UnionFrame().Pos();
+
+ Point aRet(0, 0);
+ do
+ {
+ //Does the point lie in the current ContentFrame?
+ SwRect aContentFrame( pCnt->UnionFrame() );
+ if ( aContentFrame.Contains( rPoint ) && !lcl_IsInRepeatedHeadline( pCnt ))
+ {
+ aRet = rPoint;
+ break;
+ }
+
+ //Is the current one the last ContentFrame?
+ //If the next ContentFrame lies behind the point, then the current on is the
+ //one we searched.
+ const SwContentFrame *pNxt = pCnt->GetNextContentFrame();
+ while ( pNxt && !pNxt->IsInDocBody() )
+ pNxt = pNxt->GetNextContentFrame();
+
+ //Does the point lie behind the last ContentFrame?
+ if ( !pNxt )
+ {
+ aRet = Point( aContentFrame.Right(), aContentFrame.Bottom() );
+ break;
+ }
+
+ //If the next ContentFrame lies behind the point then it is the one we
+ //searched.
+ const SwTabFrame* pTFrame;
+ pNxt->Calc(pRenderContext);
+ if( pNxt->getFrameArea().Top() > rPoint.Y() &&
+ !lcl_IsInRepeatedHeadline( pCnt, &pTFrame ) &&
+ ( !pTFrame || pNxt->getFrameArea().Left() > rPoint.X() ))
+ {
+ if (bNext)
+ aRet = pNxt->getFrameArea().Pos();
+ else
+ aRet = Point( aContentFrame.Right(), aContentFrame.Bottom() );
+ break;
+ }
+ pCnt = pNxt;
+ }
+ while (pCnt);
+ return aRet;
+}
+
+/** Returns the absolute document position of the desired page.
+ *
+ * Formatting is done only as far as needed and only if bFormat=true.
+ * Pos is set to the one of the last page, if the page number was chosen too big.
+ *
+ * @return Null, if the operation failed.
+ */
+Point SwRootFrame::GetPagePos( sal_uInt16 nPageNum ) const
+{
+ OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "No page available." );
+
+ const SwPageFrame *pPage = static_cast<const SwPageFrame*>(Lower());
+ while ( true )
+ {
+ if ( pPage->GetPhyPageNum() >= nPageNum || !pPage->GetNext() )
+ break;
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+ return pPage->getFrameArea().Pos();
+}
+
+/** get page frame by physical page number
+ *
+ * @return pointer to the page frame with the given physical page number
+ */
+SwPageFrame* SwRootFrame::GetPageByPageNum( sal_uInt16 _nPageNum ) const
+{
+ const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>( Lower() );
+ while ( pPageFrame && pPageFrame->GetPhyPageNum() < _nPageNum )
+ {
+ pPageFrame = static_cast<const SwPageFrame*>( pPageFrame->GetNext() );
+ }
+
+ if ( pPageFrame && pPageFrame->GetPhyPageNum() == _nPageNum )
+ {
+ return const_cast<SwPageFrame*>( pPageFrame );
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+/**
+ * @return true, when the given physical pagenumber doesn't exist or this page is an empty page.
+ */
+bool SwRootFrame::IsDummyPage( sal_uInt16 nPageNum ) const
+{
+ if( !Lower() || !nPageNum || nPageNum > GetPageNum() )
+ return true;
+
+ const SwPageFrame *pPage = static_cast<const SwPageFrame*>(Lower());
+ while( pPage && nPageNum < pPage->GetPhyPageNum() )
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ return !pPage || pPage->IsEmptyPage();
+}
+
+/** Is the Frame or rather the Section in which it lies protected?
+ *
+ * Also Fly in Fly in ... and Footnotes
+ */
+bool SwFrame::IsProtected() const
+{
+ if (IsTextFrame())
+ {
+ const SwDoc *pDoc = &static_cast<const SwTextFrame*>(this)->GetDoc();
+ bool isFormProtected=pDoc->GetDocumentSettingManager().get(DocumentSettingId::PROTECT_FORM );
+ if (isFormProtected)
+ {
+ return false; // TODO a hack for now, well deal with it later, I we return true here we have a "double" locking
+ }
+ }
+ //The Frame can be protected in borders, cells or sections.
+ //Also goes up FlyFrames recursive and from footnote to anchor.
+ const SwFrame *pFrame = this;
+ do
+ {
+ if (pFrame->IsTextFrame())
+ { // sw_redlinehide: redlines can't overlap section nodes, so any node will do
+ if (static_cast<SwTextFrame const*>(pFrame)->GetTextNodeFirst()->IsInProtectSect())
+ {
+ return true;
+ }
+ }
+ else if ( pFrame->IsContentFrame() )
+ {
+ assert(pFrame->IsNoTextFrame());
+ if (static_cast<const SwNoTextFrame*>(pFrame)->GetNode() &&
+ static_cast<const SwNoTextFrame*>(pFrame)->GetNode()->IsInProtectSect())
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if ( static_cast<const SwLayoutFrame*>(pFrame)->GetFormat() &&
+ static_cast<const SwLayoutFrame*>(pFrame)->GetFormat()->
+ GetProtect().IsContentProtected() )
+ return true;
+ if ( pFrame->IsCoveredCell() )
+ return true;
+ }
+ if ( pFrame->IsFlyFrame() )
+ {
+ //In a chain the protection of the content can be specified by the
+ //master of the chain.
+ if ( static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink() )
+ {
+ const SwFlyFrame *pMaster = static_cast<const SwFlyFrame*>(pFrame);
+ do
+ { pMaster = pMaster->GetPrevLink();
+ } while ( pMaster->GetPrevLink() );
+ if ( pMaster->IsProtected() )
+ return true;
+ }
+ pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
+ }
+ else if ( pFrame->IsFootnoteFrame() )
+ pFrame = static_cast<const SwFootnoteFrame*>(pFrame)->GetRef();
+ else
+ pFrame = pFrame->GetUpper();
+
+ } while ( pFrame );
+
+ return false;
+}
+
+/** @return the physical page number */
+sal_uInt16 SwFrame::GetPhyPageNum() const
+{
+ const SwPageFrame *pPage = FindPageFrame();
+ return pPage ? pPage->GetPhyPageNum() : 0;
+}
+
+/** Decides if the page want to be a right page or not.
+ *
+ * If the first content of the page has a page descriptor, we take the follow
+ * of the page descriptor of the last not empty page. If this descriptor allows
+ * only right(left) pages and the page isn't an empty page then it wants to be
+ * such right(left) page. If the descriptor allows right and left pages, we
+ * look for a number offset in the first content. If there is one, odd number
+ * results right pages (or left pages if document starts with even number),
+ * even number results left pages (or right pages if document starts with even
+ * number).
+ * If there is no number offset, we take the physical page number instead,
+ * but a previous empty page doesn't count.
+ */
+bool SwFrame::WannaRightPage() const
+{
+ const SwPageFrame *pPage = FindPageFrame();
+ if ( !pPage || !pPage->GetUpper() )
+ return true;
+
+ const SwFrame *pFlow = pPage->FindFirstBodyContent();
+ const SwPageDesc *pDesc = nullptr;
+ ::std::optional<sal_uInt16> oPgNum;
+ if ( pFlow )
+ {
+ if ( pFlow->IsInTab() )
+ pFlow = pFlow->FindTabFrame();
+ const SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
+ if ( !pTmp->IsFollow() )
+ {
+ const SwFormatPageDesc& rPgDesc = pFlow->GetPageDescItem();
+ pDesc = rPgDesc.GetPageDesc();
+ oPgNum = rPgDesc.GetNumOffset();
+ }
+ }
+ if ( !pDesc )
+ {
+ SwPageFrame *pPrv = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pPage->GetPrev()));
+ if( pPrv && pPrv->IsEmptyPage() )
+ pPrv = static_cast<SwPageFrame*>(pPrv->GetPrev());
+ if( pPrv )
+ pDesc = pPrv->GetPageDesc()->GetFollow();
+ else
+ {
+ const SwDoc* pDoc = pPage->GetFormat()->GetDoc();
+ pDesc = &pDoc->GetPageDesc( 0 );
+ }
+ }
+ OSL_ENSURE( pDesc, "No pagedescriptor" );
+ bool isRightPage;
+ if( oPgNum )
+ isRightPage = sw::IsRightPageByNumber(*mpRoot, *oPgNum);
+ else
+ {
+ isRightPage = pPage->OnRightPage();
+ if( pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
+ isRightPage = !isRightPage;
+ }
+ if( !pPage->IsEmptyPage() )
+ {
+ if( !pDesc->GetRightFormat() )
+ isRightPage = false;
+ else if( !pDesc->GetLeftFormat() )
+ isRightPage = true;
+ }
+ return isRightPage;
+}
+
+bool SwFrame::OnFirstPage() const
+{
+ bool bRet = false;
+ const SwPageFrame *pPage = FindPageFrame();
+
+ if (pPage)
+ {
+ const SwPageFrame* pPrevFrame = dynamic_cast<const SwPageFrame*>(pPage->GetPrev());
+ if (pPrevFrame)
+ {
+ // first page of layout may be empty page, but only if it starts with "Left Page" style
+ const SwPageDesc* pDesc = pPage->GetPageDesc();
+ bRet = pPrevFrame->GetPageDesc() != pDesc;
+ }
+ else
+ bRet = true;
+ }
+ return bRet;
+}
+
+void SwFrame::Calc(vcl::RenderContext* pRenderContext) const
+{
+ if ( !isFrameAreaPositionValid() || !isFramePrintAreaValid() || !isFrameAreaSizeValid() )
+ {
+ const_cast<SwFrame*>(this)->PrepareMake(pRenderContext);
+ }
+}
+
+Point SwFrame::GetRelPos() const
+{
+ Point aRet( getFrameArea().Pos() );
+ // here we cast since SwLayoutFrame is declared only as forwarded
+ aRet -= GetUpper()->getFramePrintArea().Pos();
+ aRet -= GetUpper()->getFrameArea().Pos();
+ return aRet;
+}
+
+/** @return the virtual page number with the offset. */
+sal_uInt16 SwFrame::GetVirtPageNum() const
+{
+ const SwPageFrame *pPage = FindPageFrame();
+ if ( !pPage || !pPage->GetUpper() )
+ return 0;
+
+ sal_uInt16 nPhyPage = pPage->GetPhyPageNum();
+ if ( !static_cast<const SwRootFrame*>(pPage->GetUpper())->IsVirtPageNum() )
+ return nPhyPage;
+
+ //Search the nearest section using the virtual page number.
+ //Because searching backwards needs a lot of time we search specific using
+ //the dependencies. From the PageDescs we get the attributes and from the
+ //attributes we get the sections.
+ const SwPageFrame *pVirtPage = nullptr;
+ const SwFrame *pFrame = nullptr;
+ const SfxItemPool &rPool = pPage->GetFormat()->GetDoc()->GetAttrPool();
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_PAGEDESC))
+ {
+ const SwFormatPageDesc *pDesc = dynamic_cast<const SwFormatPageDesc*>(pItem);
+ if ( !pDesc )
+ continue;
+
+ if ( pDesc->GetNumOffset() && pDesc->GetDefinedIn() )
+ {
+ const sw::BroadcastingModify *pMod = pDesc->GetDefinedIn();
+ SwVirtPageNumInfo aInfo( pPage );
+ pMod->GetInfo( aInfo );
+ if ( aInfo.GetPage() )
+ {
+ if( !pVirtPage || aInfo.GetPage()->GetPhyPageNum() > pVirtPage->GetPhyPageNum() )
+ {
+ pVirtPage = aInfo.GetPage();
+ pFrame = aInfo.GetFrame();
+ }
+ }
+ }
+ }
+ if ( pFrame )
+ {
+ ::std::optional<sal_uInt16> oNumOffset = pFrame->GetPageDescItem().GetNumOffset();
+ if (oNumOffset)
+ {
+ return nPhyPage - pFrame->GetPhyPageNum() + *oNumOffset;
+ }
+ else
+ {
+ return nPhyPage - pFrame->GetPhyPageNum();
+ }
+ }
+ return nPhyPage;
+}
+
+/** Determines and sets those cells which are enclosed by the selection. */
+bool SwRootFrame::MakeTableCursors( SwTableCursor& rTableCursor )
+{
+ //Find Union-Rects and tables (Follows) of the selection.
+ OSL_ENSURE( rTableCursor.GetContentNode() && rTableCursor.GetContentNode( false ),
+ "Tabselection not on Cnt." );
+
+ bool bRet = false;
+
+ // For new table models there's no need to ask the layout...
+ if( rTableCursor.NewTableSelection() )
+ return true;
+
+ Point aPtPt, aMkPt;
+ {
+ SwShellCursor* pShCursor = dynamic_cast<SwShellCursor*>(&rTableCursor);
+
+ if( pShCursor )
+ {
+ aPtPt = pShCursor->GetPtPos();
+ aMkPt = pShCursor->GetMkPos();
+ }
+ }
+
+ // #151012# Made code robust here
+ const SwContentNode* pTmpStartNode = rTableCursor.GetContentNode();
+ const SwContentNode* pTmpEndNode = rTableCursor.GetContentNode(false);
+
+ std::pair<Point, bool> tmp(aPtPt, false);
+ const SwFrame *const pTmpStartFrame = pTmpStartNode ? pTmpStartNode->getLayoutFrame(this, nullptr, &tmp) : nullptr;
+ tmp.first = aMkPt;
+ const SwFrame *const pTmpEndFrame = pTmpEndNode ? pTmpEndNode->getLayoutFrame(this, nullptr, &tmp) : nullptr;
+
+ const SwLayoutFrame* pStart = pTmpStartFrame ? pTmpStartFrame->GetUpper() : nullptr;
+ const SwLayoutFrame* pEnd = pTmpEndFrame ? pTmpEndFrame->GetUpper() : nullptr;
+
+ OSL_ENSURE( pStart && pEnd, "MakeTableCursors: Good to have the code robust here!" );
+
+ /* #109590# Only change table boxes if the frames are
+ valid. Needed because otherwise the table cursor after moving
+ table cells by dnd resulted in an empty tables cursor. */
+ if ( pStart && pEnd && pStart->isFrameAreaDefinitionValid() && pEnd->isFrameAreaDefinitionValid())
+ {
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd );
+
+ SwSelBoxes aNew;
+
+ const bool bReadOnlyAvailable = rTableCursor.IsReadOnlyAvailable();
+
+ for (SwSelUnion & rUnion : aUnions)
+ {
+ const SwTabFrame *pTable = rUnion.GetTable();
+
+ // Skip any repeated headlines in the follow:
+ SwLayoutFrame* pRow = pTable->IsFollow() ?
+ pTable->GetFirstNonHeadlineRow() :
+ const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pTable->Lower()));
+
+ while ( pRow )
+ {
+ if ( pRow->getFrameArea().Overlaps( rUnion.GetUnion() ) )
+ {
+ const SwLayoutFrame *pCell = pRow->FirstCell();
+
+ while ( pCell && pRow->IsAnLower( pCell ) )
+ {
+ OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" );
+ if( IsFrameInTableSel( rUnion.GetUnion(), pCell ) &&
+ (bReadOnlyAvailable ||
+ !pCell->GetFormat()->GetProtect().IsContentProtected()))
+ {
+ SwTableBox* pInsBox = const_cast<SwTableBox*>(
+ static_cast<const SwCellFrame*>(pCell)->GetTabBox());
+ aNew.insert( pInsBox );
+ }
+ if ( pCell->GetNext() )
+ {
+ pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
+ if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
+ pCell = pCell->FirstCell();
+ }
+ else
+ {
+ const SwLayoutFrame* pLastCell = pCell;
+ do
+ {
+ pCell = pCell->GetNextLayoutLeaf();
+ } while ( pCell && pLastCell->IsAnLower( pCell ) );
+ // For sections with columns
+ if( pCell && pCell->IsInTab() )
+ {
+ while( !pCell->IsCellFrame() )
+ {
+ pCell = pCell->GetUpper();
+ OSL_ENSURE( pCell, "Where's my cell?" );
+ }
+ }
+ }
+ }
+ }
+ pRow = static_cast<SwLayoutFrame*>(pRow->GetNext());
+ }
+ }
+
+ rTableCursor.ActualizeSelection( aNew );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+static void Sub( SwRegionRects& rRegion, const SwRect& rRect )
+{
+ if( rRect.Width() > 1 && rRect.Height() > 1 &&
+ rRect.Overlaps( rRegion.GetOrigin() ))
+ rRegion -= rRect;
+}
+
+static void Add( SwRegionRects& rRegion, const SwRect& rRect )
+{
+ if( rRect.Width() > 1 && rRect.Height() > 1 )
+ rRegion += rRect;
+}
+
+/*
+ * The following situations can happen:
+ * 1. Start and end lie in one screen-row and in the same node
+ * -> one rectangle out of start and end; and we're okay
+ * 2. Start and end lie in one frame (therefore in the same node!)
+ * -> expand start to the right, end to the left and if more than two
+ * screen-rows are involved - calculate the in-between
+ * 3. Start and end lie in different frames
+ * -> expand start to the right until frame-end, calculate Rect
+ * expand end to the left until frame-start, calculate Rect
+ * and if more than two frames are involved add the PrtArea of all
+ * frames which lie in between
+ *
+ * Big reorganization because of the FlyFrame - those need to be locked out.
+ * Exceptions: - The Fly in which the selection took place (if it took place
+ * in a Fly)
+ * - The Flys which are underrun by the text
+ * - The Flys which are anchored to somewhere inside the selection.
+ * Functioning: First a SwRegion with a root gets initialized.
+ * Out of the region the inverted sections are cut out. The
+ * section gets compressed and finally inverted and thereby the
+ * inverted rectangles are available.
+ * In the end the Flys are cut out of the section.
+ */
+void SwRootFrame::CalcFrameRects(SwShellCursor &rCursor)
+{
+ SwPosition *pStartPos = rCursor.Start(),
+ *pEndPos = rCursor.End();
+
+ SwViewShell *pSh = GetCurrShell();
+
+ bool bIgnoreVisArea = true;
+ if (pSh)
+ bIgnoreVisArea = pSh->GetViewOptions()->IsPDFExport() || comphelper::LibreOfficeKit::isActive();
+
+ // #i12836# enhanced pdf
+ SwRegionRects aRegion( !bIgnoreVisArea ?
+ pSh->VisArea() :
+ getFrameArea() );
+ if( !pStartPos->nNode.GetNode().IsContentNode() ||
+ !pStartPos->nNode.GetNode().GetContentNode()->getLayoutFrame(this) ||
+ ( pStartPos->nNode != pEndPos->nNode &&
+ ( !pEndPos->nNode.GetNode().IsContentNode() ||
+ !pEndPos->nNode.GetNode().GetContentNode()->getLayoutFrame(this) ) ) )
+ {
+ return;
+ }
+
+ DisableCallbackAction a(*this); // the GetCharRect below may format
+
+ //First obtain the ContentFrames for the start and the end - those are needed
+ //anyway.
+ std::pair<Point, bool> tmp(rCursor.GetSttPos(), true);
+ SwContentFrame* pStartFrame = pStartPos->nNode.GetNode().
+ GetContentNode()->getLayoutFrame(this, pStartPos, &tmp);
+
+ tmp.first = rCursor.GetEndPos();
+ SwContentFrame* pEndFrame = pEndPos->nNode.GetNode().
+ GetContentNode()->getLayoutFrame(this, pEndPos, &tmp);
+
+ assert(pStartFrame && pEndFrame && "No ContentFrames found.");
+ //tdf#119224 start and end are expected to exist for the scope of this function
+ SwFrameDeleteGuard aStartFrameGuard(pStartFrame), aEndFrameGuard(pEndFrame);
+
+ //Do not subtract the FlyFrames in which selected Frames lie.
+ SwSortedObjs aSortObjs;
+ if ( pStartFrame->IsInFly() )
+ {
+ const SwAnchoredObject* pObj = pStartFrame->FindFlyFrame();
+ OSL_ENSURE( pObj, "No Start Object." );
+ if (pObj) aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj) );
+ const SwAnchoredObject* pObj2 = pEndFrame->FindFlyFrame();
+ OSL_ENSURE( pObj2, "SwRootFrame::CalcFrameRects(..) - FlyFrame missing - looks like an invalid selection" );
+ if ( pObj2 != nullptr && pObj2 != pObj )
+ {
+ aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj2) );
+ }
+ }
+
+ // if a selection which is not allowed exists, we correct what is not
+ // allowed (header/footer/table-headline) for two pages.
+ do { // middle check loop
+ const SwLayoutFrame* pSttLFrame = pStartFrame->GetUpper();
+ const SwFrameType cHdFtTableHd = SwFrameType::Header | SwFrameType::Footer | SwFrameType::Tab;
+ while( pSttLFrame &&
+ ! (cHdFtTableHd & pSttLFrame->GetType() ))
+ pSttLFrame = pSttLFrame->GetUpper();
+ if( !pSttLFrame )
+ break;
+ const SwLayoutFrame* pEndLFrame = pEndFrame->GetUpper();
+ while( pEndLFrame &&
+ ! (cHdFtTableHd & pEndLFrame->GetType() ))
+ pEndLFrame = pEndLFrame->GetUpper();
+ if( !pEndLFrame )
+ break;
+
+ OSL_ENSURE( pEndLFrame->GetType() == pSttLFrame->GetType(),
+ "Selection over different content" );
+ switch( pSttLFrame->GetType() )
+ {
+ case SwFrameType::Header:
+ case SwFrameType::Footer:
+ // On different pages? Then always on the start-page
+ if( pEndLFrame->FindPageFrame() != pSttLFrame->FindPageFrame() )
+ {
+ // Set end- to the start-ContentFrame
+ if( pStartPos == rCursor.GetPoint() )
+ pEndFrame = pStartFrame;
+ else
+ pStartFrame = pEndFrame;
+ }
+ break;
+ case SwFrameType::Tab:
+ // On different pages? Then check for table-headline
+ {
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pSttLFrame);
+ if( ( pTabFrame->GetFollow() ||
+ static_cast<const SwTabFrame*>(pEndLFrame)->GetFollow() ) &&
+ pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
+ pTabFrame->GetLower() != static_cast<const SwTabFrame*>(pEndLFrame)->GetLower() &&
+ ( lcl_IsInRepeatedHeadline( pStartFrame ) ||
+ lcl_IsInRepeatedHeadline( pEndFrame ) ) )
+ {
+ // Set end- to the start-ContentFrame
+ if( pStartPos == rCursor.GetPoint() )
+ pEndFrame = pStartFrame;
+ else
+ pStartFrame = pEndFrame;
+ }
+ }
+ break;
+ default: break;
+ }
+ } while( false );
+
+ SwCursorMoveState aTmpState( CursorMoveState::NONE );
+ aTmpState.m_b2Lines = true;
+ aTmpState.m_bNoScroll = true;
+ aTmpState.m_nCursorBidiLevel = pStartFrame->IsRightToLeft() ? 1 : 0;
+
+ //ContentRects to Start- and EndFrames.
+ SwRect aStRect, aEndRect;
+ pStartFrame->GetCharRect( aStRect, *pStartPos, &aTmpState );
+ std::unique_ptr<Sw2LinesPos> pSt2Pos = std::move(aTmpState.m_p2Lines);
+ aTmpState.m_nCursorBidiLevel = pEndFrame->IsRightToLeft() ? 1 : 0;
+
+ pEndFrame->GetCharRect( aEndRect, *pEndPos, &aTmpState );
+ std::unique_ptr<Sw2LinesPos> pEnd2Pos = std::move(aTmpState.m_p2Lines);
+
+ SwRect aStFrame ( pStartFrame->UnionFrame( true ) );
+ aStFrame.Intersection( pStartFrame->GetPaintArea() );
+ SwRect aEndFrame( pStartFrame == pEndFrame ? aStFrame : pEndFrame->UnionFrame( true ) );
+ if( pStartFrame != pEndFrame )
+ {
+ aEndFrame.Intersection( pEndFrame->GetPaintArea() );
+ }
+ SwRectFnSet aRectFnSet(pStartFrame);
+ const bool bR2L = pStartFrame->IsRightToLeft();
+ const bool bEndR2L = pEndFrame->IsRightToLeft();
+ const bool bB2T = pStartFrame->IsVertLRBT();
+
+ // If there's no doubleline portion involved or start and end are both
+ // in the same doubleline portion, all works fine, but otherwise
+ // we need the following...
+ if( pSt2Pos != pEnd2Pos && ( !pSt2Pos || !pEnd2Pos ||
+ pSt2Pos->aPortion != pEnd2Pos->aPortion ) )
+ {
+ // If we have a start(end) position inside a doubleline portion
+ // the surrounded part of the doubleline portion is subtracted
+ // from the region and the aStRect(aEndRect) is set to the
+ // end(start) of the doubleline portion.
+ if( pSt2Pos )
+ {
+ SwRect aTmp( aStRect );
+
+ // BiDi-Portions are swimming against the current.
+ const bool bPorR2L = ( MultiPortionType::BIDI == pSt2Pos->nMultiType ) ?
+ ! bR2L :
+ bR2L;
+
+ if( MultiPortionType::BIDI == pSt2Pos->nMultiType &&
+ aRectFnSet.GetWidth(pSt2Pos->aPortion2) )
+ {
+ // nested bidi portion
+ tools::Long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion);
+ nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2);
+ tools::Long nLeftAbs = nRightAbs - aRectFnSet.GetWidth(pSt2Pos->aPortion2);
+
+ aRectFnSet.SetRight( aTmp, nRightAbs );
+
+ if ( ! pEnd2Pos || pEnd2Pos->aPortion != pSt2Pos->aPortion )
+ {
+ SwRect aTmp2( pSt2Pos->aPortion );
+ aRectFnSet.SetRight( aTmp2, nLeftAbs );
+ aTmp2.Intersection( aEndFrame );
+ Sub( aRegion, aTmp2 );
+ }
+ }
+ else
+ {
+ if( bPorR2L )
+ aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(pSt2Pos->aPortion) );
+ else
+ aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(pSt2Pos->aPortion) );
+ }
+
+ if( MultiPortionType::ROT_90 == pSt2Pos->nMultiType ||
+ aRectFnSet.GetTop(pSt2Pos->aPortion) ==
+ aRectFnSet.GetTop(aTmp) )
+ {
+ aRectFnSet.SetTop( aTmp, aRectFnSet.GetTop(pSt2Pos->aLine) );
+ }
+
+ aTmp.Intersection( aStFrame );
+ Sub( aRegion, aTmp );
+
+ SwTwips nTmp = aRectFnSet.GetBottom(pSt2Pos->aLine);
+ if( MultiPortionType::ROT_90 != pSt2Pos->nMultiType &&
+ aRectFnSet.BottomDist( aStRect, nTmp ) > 0 )
+ {
+ aRectFnSet.SetTop( aTmp, aRectFnSet.GetBottom(aTmp) );
+ aRectFnSet.SetBottom( aTmp, nTmp );
+ if( aRectFnSet.BottomDist( aStRect, aRectFnSet.GetBottom(pSt2Pos->aPortion) ) > 0 )
+ {
+ if( bPorR2L )
+ aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(pSt2Pos->aPortion) );
+ else
+ aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(pSt2Pos->aPortion) );
+ }
+ aTmp.Intersection( aStFrame );
+ Sub( aRegion, aTmp );
+ }
+
+ aStRect = pSt2Pos->aLine;
+ aRectFnSet.SetLeft( aStRect, bR2L ?
+ aRectFnSet.GetLeft(pSt2Pos->aPortion) :
+ aRectFnSet.GetRight(pSt2Pos->aPortion) );
+ aRectFnSet.SetWidth( aStRect, 1 );
+ }
+
+ if( pEnd2Pos )
+ {
+ SwRectFnSet fnRectX(pEndFrame);
+ SwRect aTmp( aEndRect );
+
+ // BiDi-Portions are swimming against the current.
+ const bool bPorR2L = ( MultiPortionType::BIDI == pEnd2Pos->nMultiType ) ?
+ ! bEndR2L :
+ bEndR2L;
+
+ if( MultiPortionType::BIDI == pEnd2Pos->nMultiType &&
+ fnRectX.GetWidth(pEnd2Pos->aPortion2) )
+ {
+ // nested bidi portion
+ tools::Long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion);
+ nRightAbs = nRightAbs - fnRectX.GetLeft(pEnd2Pos->aPortion2);
+ tools::Long nLeftAbs = nRightAbs - fnRectX.GetWidth(pEnd2Pos->aPortion2);
+
+ fnRectX.SetLeft( aTmp, nLeftAbs );
+
+ if ( ! pSt2Pos || pSt2Pos->aPortion != pEnd2Pos->aPortion )
+ {
+ SwRect aTmp2( pEnd2Pos->aPortion );
+ fnRectX.SetLeft( aTmp2, nRightAbs );
+ aTmp2.Intersection( aEndFrame );
+ Sub( aRegion, aTmp2 );
+ }
+ }
+ else
+ {
+ if ( bPorR2L )
+ fnRectX.SetRight( aTmp, fnRectX.GetRight(pEnd2Pos->aPortion) );
+ else
+ fnRectX.SetLeft( aTmp, fnRectX.GetLeft(pEnd2Pos->aPortion) );
+ }
+
+ if( MultiPortionType::ROT_90 == pEnd2Pos->nMultiType ||
+ fnRectX.GetBottom(pEnd2Pos->aPortion) ==
+ fnRectX.GetBottom(aEndRect) )
+ {
+ fnRectX.SetBottom( aTmp, fnRectX.GetBottom(pEnd2Pos->aLine) );
+ }
+
+ aTmp.Intersection( aEndFrame );
+ Sub( aRegion, aTmp );
+
+ // The next statement means neither ruby nor rotate(90):
+ if( MultiPortionType::RUBY != pEnd2Pos->nMultiType && MultiPortionType::ROT_90 != pEnd2Pos->nMultiType )
+ {
+ SwTwips nTmp = fnRectX.GetTop(pEnd2Pos->aLine);
+ if( fnRectX.GetTop(aEndRect) != nTmp )
+ {
+ fnRectX.SetBottom( aTmp, fnRectX.GetTop(aTmp) );
+ fnRectX.SetTop( aTmp, nTmp );
+ if( fnRectX.GetTop(aEndRect) !=
+ fnRectX.GetTop(pEnd2Pos->aPortion) )
+ {
+ if( bPorR2L )
+ fnRectX.SetLeft( aTmp, fnRectX.GetLeft(pEnd2Pos->aPortion) );
+ else
+ fnRectX.SetRight( aTmp, fnRectX.GetRight(pEnd2Pos->aPortion) );
+ }
+ aTmp.Intersection( aEndFrame );
+ Sub( aRegion, aTmp );
+ }
+ }
+
+ aEndRect = pEnd2Pos->aLine;
+ fnRectX.SetLeft( aEndRect, bEndR2L ?
+ fnRectX.GetRight(pEnd2Pos->aPortion) :
+ fnRectX.GetLeft(pEnd2Pos->aPortion) );
+ fnRectX.SetWidth( aEndRect, 1 );
+ }
+ }
+ else if( pSt2Pos && pEnd2Pos &&
+ MultiPortionType::BIDI == pSt2Pos->nMultiType &&
+ MultiPortionType::BIDI == pEnd2Pos->nMultiType &&
+ pSt2Pos->aPortion == pEnd2Pos->aPortion &&
+ pSt2Pos->aPortion2 != pEnd2Pos->aPortion2 )
+ {
+ // This is the ugly special case, where the selection starts and
+ // ends in the same bidi portion but one start or end is inside a
+ // nested bidi portion.
+
+ if ( aRectFnSet.GetWidth(pSt2Pos->aPortion2) )
+ {
+ SwRect aTmp( aStRect );
+ tools::Long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion);
+ nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2);
+ tools::Long nLeftAbs = nRightAbs - aRectFnSet.GetWidth(pSt2Pos->aPortion2);
+
+ aRectFnSet.SetRight( aTmp, nRightAbs );
+ aTmp.Intersection( aStFrame );
+ Sub( aRegion, aTmp );
+
+ aStRect = pSt2Pos->aLine;
+ aRectFnSet.SetLeft( aStRect, bR2L ? nRightAbs : nLeftAbs );
+ aRectFnSet.SetWidth( aStRect, 1 );
+ }
+
+ SwRectFnSet fnRectX(pEndFrame);
+ if ( fnRectX.GetWidth(pEnd2Pos->aPortion2) )
+ {
+ SwRect aTmp( aEndRect );
+ tools::Long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion);
+ nRightAbs -= fnRectX.GetLeft(pEnd2Pos->aPortion2);
+ tools::Long nLeftAbs = nRightAbs - fnRectX.GetWidth(pEnd2Pos->aPortion2);
+
+ fnRectX.SetLeft( aTmp, nLeftAbs );
+ aTmp.Intersection( aEndFrame );
+ Sub( aRegion, aTmp );
+
+ aEndRect = pEnd2Pos->aLine;
+ fnRectX.SetLeft( aEndRect, bEndR2L ? nLeftAbs : nRightAbs );
+ fnRectX.SetWidth( aEndRect, 1 );
+ }
+ }
+
+ // The charrect may be outside the paintarea (for cursortravelling)
+ // but the selection has to be restricted to the paintarea
+ if( aStRect.Left() < aStFrame.Left() )
+ aStRect.Left( aStFrame.Left() );
+ else if( aStRect.Left() > aStFrame.Right() )
+ aStRect.Left( aStFrame.Right() );
+ SwTwips nTmp = aStRect.Right();
+ if( nTmp < aStFrame.Left() )
+ aStRect.Right( aStFrame.Left() );
+ else if( nTmp > aStFrame.Right() )
+ aStRect.Right( aStFrame.Right() );
+ if( aEndRect.Left() < aEndFrame.Left() )
+ aEndRect.Left( aEndFrame.Left() );
+ else if( aEndRect.Left() > aEndFrame.Right() )
+ aEndRect.Left( aEndFrame.Right() );
+ nTmp = aEndRect.Right();
+ if( nTmp < aEndFrame.Left() )
+ aEndRect.Right( aEndFrame.Left() );
+ else if( nTmp > aEndFrame.Right() )
+ aEndRect.Right( aEndFrame.Right() );
+
+ if( pStartFrame == pEndFrame )
+ {
+ bool bSameRotatedOrBidi = pSt2Pos && pEnd2Pos &&
+ ( MultiPortionType::BIDI == pSt2Pos->nMultiType ||
+ MultiPortionType::ROT_270 == pSt2Pos->nMultiType ||
+ MultiPortionType::ROT_90 == pSt2Pos->nMultiType ) &&
+ pSt2Pos->aPortion == pEnd2Pos->aPortion;
+ //case 1: (Same frame and same row)
+ if( bSameRotatedOrBidi ||
+ aRectFnSet.GetTop(aStRect) == aRectFnSet.GetTop(aEndRect) )
+ {
+ Point aTmpSt( aStRect.Pos() );
+ Point aTmpEnd( aEndRect.Right(), aEndRect.Bottom() );
+ if (bSameRotatedOrBidi || bR2L || bB2T)
+ {
+ if( aTmpSt.Y() > aTmpEnd.Y() )
+ {
+ tools::Long nTmpY = aTmpEnd.Y();
+ aTmpEnd.setY( aTmpSt.Y() );
+ aTmpSt.setY( nTmpY );
+ }
+ if( aTmpSt.X() > aTmpEnd.X() )
+ {
+ tools::Long nTmpX = aTmpEnd.X();
+ aTmpEnd.setX( aTmpSt.X() );
+ aTmpSt.setX( nTmpX );
+ }
+ }
+
+ SwRect aTmp( aTmpSt, aTmpEnd );
+ // Bug 34888: If content is selected which doesn't take space
+ // away (i.e. PostIts, RefMarks, TOXMarks), then at
+ // least set the width of the Cursor.
+ if( 1 == aRectFnSet.GetWidth(aTmp) &&
+ pStartPos->nContent.GetIndex() !=
+ pEndPos->nContent.GetIndex() )
+ {
+ OutputDevice* pOut = pSh->GetOut();
+ tools::Long nCursorWidth = pOut->GetSettings().GetStyleSettings().
+ GetCursorSize();
+ aRectFnSet.SetWidth( aTmp, pOut->PixelToLogic(
+ Size( nCursorWidth, 0 ) ).Width() );
+ }
+ aTmp.Intersection( aStFrame );
+ Sub( aRegion, aTmp );
+ }
+ //case 2: (Same frame, but not the same line)
+ else
+ {
+ SwTwips lLeft, lRight;
+ if( pSt2Pos && pEnd2Pos && pSt2Pos->aPortion == pEnd2Pos->aPortion )
+ {
+ lLeft = aRectFnSet.GetLeft(pSt2Pos->aPortion);
+ lRight = aRectFnSet.GetRight(pSt2Pos->aPortion);
+ }
+ else
+ {
+ lLeft = aRectFnSet.GetLeft(pStartFrame->getFrameArea()) +
+ aRectFnSet.GetLeft(pStartFrame->getFramePrintArea());
+ lRight = aRectFnSet.GetRight(aEndFrame);
+ }
+ if( lLeft < aRectFnSet.GetLeft(aStFrame) )
+ lLeft = aRectFnSet.GetLeft(aStFrame);
+ if( lRight > aRectFnSet.GetRight(aStFrame) )
+ lRight = aRectFnSet.GetRight(aStFrame);
+ SwRect aSubRect( aStRect );
+ //First line
+ if( bR2L )
+ aRectFnSet.SetLeft( aSubRect, lLeft );
+ else
+ aRectFnSet.SetRight( aSubRect, lRight );
+ Sub( aRegion, aSubRect );
+
+ //If there's at least a twips between start- and endline,
+ //so the whole area between will be added.
+ SwTwips aTmpBottom = aRectFnSet.GetBottom(aStRect);
+ SwTwips aTmpTop = aRectFnSet.GetTop(aEndRect);
+ if( aTmpBottom != aTmpTop )
+ {
+ aRectFnSet.SetLeft( aSubRect, lLeft );
+ aRectFnSet.SetRight( aSubRect, lRight );
+ aRectFnSet.SetTop( aSubRect, aTmpBottom );
+ aRectFnSet.SetBottom( aSubRect, aTmpTop );
+ Sub( aRegion, aSubRect );
+ }
+ //and the last line
+ aSubRect = aEndRect;
+ if( bR2L )
+ aRectFnSet.SetRight( aSubRect, lRight );
+ else
+ aRectFnSet.SetLeft( aSubRect, lLeft );
+ Sub( aRegion, aSubRect );
+ }
+ }
+ //case 3: (Different frames, maybe with other frames between)
+ else
+ {
+ //The startframe first...
+ SwRect aSubRect( aStRect );
+ if( bR2L )
+ aRectFnSet.SetLeft( aSubRect, aRectFnSet.GetLeft(aStFrame));
+ else
+ aRectFnSet.SetRight( aSubRect, aRectFnSet.GetRight(aStFrame));
+ Sub( aRegion, aSubRect );
+ SwTwips nTmpTwips = aRectFnSet.GetBottom(aStRect);
+ if( aRectFnSet.GetBottom(aStFrame) != nTmpTwips )
+ {
+ aSubRect = aStFrame;
+ aRectFnSet.SetTop( aSubRect, nTmpTwips );
+ Sub( aRegion, aSubRect );
+ }
+
+ //Now the frames between, if there are any
+ bool const bBody = pStartFrame->IsInDocBody();
+ const SwTableBox* pCellBox = pStartFrame->GetUpper()->IsCellFrame() ?
+ static_cast<const SwCellFrame*>(pStartFrame->GetUpper())->GetTabBox() : nullptr;
+ if (pSh->IsSelectAll())
+ pCellBox = nullptr;
+
+ const SwContentFrame *pContent = pStartFrame->GetNextContentFrame();
+ SwRect aPrvRect;
+
+ OSL_ENSURE( pContent,
+ "<SwRootFrame::CalcFrameRects(..)> - no content frame. This is a serious defect" );
+ while ( pContent && pContent != pEndFrame )
+ {
+ if ( pContent->IsInFly() )
+ {
+ const SwAnchoredObject* pObj = pContent->FindFlyFrame();
+ if (!aSortObjs.Contains(*pObj))
+ { // is this even possible, assuming valid cursor pos.?
+ aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj) );
+ }
+ }
+
+ // Consider only frames which have the same IsInDocBody value like pStartFrame
+ // If pStartFrame is inside a SwCellFrame, consider only frames which are inside the
+ // same cell frame (or its follow cell)
+ const SwTableBox* pTmpCellBox = pContent->GetUpper()->IsCellFrame() ?
+ static_cast<const SwCellFrame*>(pContent->GetUpper())->GetTabBox() : nullptr;
+ if (pSh->IsSelectAll())
+ pTmpCellBox = nullptr;
+ if ( bBody == pContent->IsInDocBody() &&
+ ( !pCellBox || pCellBox == pTmpCellBox ) )
+ {
+ SwRect aCRect( pContent->UnionFrame( true ) );
+ aCRect.Intersection( pContent->GetPaintArea() );
+ if( aCRect.Overlaps( aRegion.GetOrigin() ))
+ {
+ SwRect aTmp( aPrvRect );
+ aTmp.Union( aCRect );
+ if ( (aPrvRect.Height() * aPrvRect.Width() +
+ aCRect.Height() * aCRect.Width()) ==
+ (aTmp.Height() * aTmp.Width()) )
+ {
+ aPrvRect.Union( aCRect );
+ }
+ else
+ {
+ if ( aPrvRect.HasArea() )
+ Sub( aRegion, aPrvRect );
+ aPrvRect = aCRect;
+ }
+ }
+ }
+ pContent = pContent->GetNextContentFrame();
+ OSL_ENSURE( pContent,
+ "<SwRootFrame::CalcFrameRects(..)> - no content frame. This is a serious defect!" );
+ }
+ if ( aPrvRect.HasArea() )
+ Sub( aRegion, aPrvRect );
+
+ //At least the endframe...
+ aRectFnSet.Refresh(pEndFrame);
+ nTmpTwips = aRectFnSet.GetTop(aEndRect);
+ if( aRectFnSet.GetTop(aEndFrame) != nTmpTwips )
+ {
+ aSubRect = aEndFrame;
+ aRectFnSet.SetBottom( aSubRect, nTmpTwips );
+ Sub( aRegion, aSubRect );
+ }
+ aSubRect = aEndRect;
+ if( bEndR2L )
+ aRectFnSet.SetRight(aSubRect, aRectFnSet.GetRight(aEndFrame));
+ else
+ aRectFnSet.SetLeft( aSubRect, aRectFnSet.GetLeft(aEndFrame) );
+ Sub( aRegion, aSubRect );
+ }
+
+ aRegion.Invert();
+ pSt2Pos.reset();
+ pEnd2Pos.reset();
+
+ // Cut out Flys during loop. We don't cut out Flys when:
+ // - the Lower is StartFrame/EndFrame (FlyInCnt and all other Flys which again
+ // sit in it)
+ // - if in the Z-order we have Flys above those in which the StartFrame is
+ // placed
+ // - if they are anchored to inside the selection and thus part of it
+ const SwPageFrame *pPage = pStartFrame->FindPageFrame();
+ const SwPageFrame *pEndPage = pEndFrame->FindPageFrame();
+
+ while ( pPage )
+ {
+ if ( pPage->GetSortedObjs() )
+ {
+ const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
+ if ( !pFly )
+ continue;
+ const SwVirtFlyDrawObj* pObj = pFly->GetVirtDrawObj();
+ const SwFormatSurround &rSur = pFly->GetFormat()->GetSurround();
+ SwFormatAnchor const& rAnchor(pAnchoredObj->GetFrameFormat().GetAnchor());
+ const SwPosition* anchoredAt = rAnchor.GetContentAnchor();
+ bool inSelection = (
+ anchoredAt != nullptr
+ && ( (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
+ && IsDestroyFrameAnchoredAtChar(*anchoredAt, *pStartPos, *pEndPos))
+ || (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
+ && IsSelectFrameAnchoredAtPara(*anchoredAt, *pStartPos, *pEndPos))));
+ if( inSelection )
+ Add( aRegion, pFly->getFrameArea() );
+ else if ( !pFly->IsAnLower( pStartFrame ) &&
+ (rSur.GetSurround() != css::text::WrapTextMode_THROUGH &&
+ !rSur.IsContour()) )
+ {
+ if ( aSortObjs.Contains( *pAnchoredObj ) )
+ continue;
+
+ bool bSub = true;
+ const sal_uInt32 nPos = pObj->GetOrdNum();
+ for ( size_t k = 0; bSub && k < aSortObjs.size(); ++k )
+ {
+ assert( dynamic_cast< const SwFlyFrame *>( aSortObjs[k] ) &&
+ "<SwRootFrame::CalcFrameRects(..)> - object in <aSortObjs> of unexpected type" );
+ const SwFlyFrame* pTmp = static_cast<SwFlyFrame*>(aSortObjs[k]);
+ do
+ {
+ if ( nPos < pTmp->GetVirtDrawObj()->GetOrdNumDirect() )
+ {
+ bSub = false;
+ }
+ else
+ {
+ pTmp = pTmp->GetAnchorFrame()->FindFlyFrame();
+ }
+ } while ( bSub && pTmp );
+ }
+ if ( bSub )
+ Sub( aRegion, pFly->getFrameArea() );
+ }
+ }
+ }
+ if ( pPage == pEndPage )
+ break;
+ else
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+
+ //Because it looks better, we close the DropCaps.
+ SwRect aDropRect;
+ if ( pStartFrame->IsTextFrame() )
+ {
+ if ( static_cast<const SwTextFrame*>(pStartFrame)->GetDropRect( aDropRect ) )
+ Sub( aRegion, aDropRect );
+ }
+ if ( pEndFrame != pStartFrame && pEndFrame->IsTextFrame() )
+ {
+ if ( static_cast<const SwTextFrame*>(pEndFrame)->GetDropRect( aDropRect ) )
+ Sub( aRegion, aDropRect );
+ }
+
+ rCursor.assign( aRegion.begin(), aRegion.end() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/unusedf.cxx b/sw/source/core/layout/unusedf.cxx
new file mode 100644
index 000000000..25de7617c
--- /dev/null
+++ b/sw/source/core/layout/unusedf.cxx
@@ -0,0 +1,79 @@
+/* -*- 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 <rootfrm.hxx>
+#include <cntfrm.hxx>
+#include <flyfrm.hxx>
+#include <osl/diagnose.h>
+
+void SwFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
+{
+ OSL_FAIL( "Format() of the base class called." );
+}
+
+void SwFrame::PaintSwFrame(vcl::RenderContext&, SwRect const&, SwPrintData const*const) const
+{
+ OSL_FAIL( "PaintSwFrame() of the base class called." );
+}
+
+bool SwContentFrame::WouldFit(SwTwips &, bool&, bool, bool)
+{
+ OSL_FAIL( "WouldFit of ContentFrame called." );
+ return false;
+}
+
+bool SwFrame::FillSelection( SwSelectionList& , const SwRect& ) const
+{
+ OSL_FAIL( "Don't call this function at the base class!" );
+ return false;
+}
+
+bool SwFrame::GetModelPositionForViewPoint( SwPosition *, Point&, SwCursorMoveState*, bool ) const
+{
+ OSL_FAIL( "GetModelPositionForViewPoint of the base class, hi!" );
+ return false;
+}
+
+#ifdef DBG_UTIL
+
+void SwRootFrame::Cut()
+{
+ OSL_FAIL( "Cut() of RootFrame called." );
+}
+
+void SwRootFrame::Paste( SwFrame *, SwFrame * )
+{
+ OSL_FAIL( "Paste() of RootFrame called." );
+}
+
+void SwFlyFrame::Paste( SwFrame *, SwFrame * )
+{
+ OSL_FAIL( "Paste() of FlyFrame called." );
+}
+
+#endif
+
+bool SwFrame::GetCharRect( SwRect&, const SwPosition&,
+ SwCursorMoveState*, bool ) const
+{
+ OSL_FAIL( "GetCharRect() of the base called." );
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/virtoutp.cxx b/sw/source/core/layout/virtoutp.cxx
new file mode 100644
index 000000000..dd044317d
--- /dev/null
+++ b/sw/source/core/layout/virtoutp.cxx
@@ -0,0 +1,189 @@
+/* -*- 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 "virtoutp.hxx"
+#include <viewopt.hxx>
+#include <rootfrm.hxx>
+#include <osl/diagnose.h>
+
+/* The SWLayVout class manages the virtual output devices.
+ * RootFrame has a static member of this class which is created in FrameInit
+ * and destroyed in FrameFinit.
+ * */
+
+bool SwRootFrame::FlushVout()
+{
+ if (SwRootFrame::s_pVout->IsFlushable())
+ {
+ SwRootFrame::s_pVout->Flush_();
+ return true;
+ }
+ return false;
+}
+
+bool SwRootFrame::HasSameRect( const SwRect& rRect )
+{
+ if (SwRootFrame::s_pVout->IsFlushable())
+ return ( rRect == SwRootFrame::s_pVout->GetOrgRect() );
+ return false;
+}
+
+/** method to set mapping/pixel offset for virtual output device
+
+ OD 12.11.2002 #96272# - method implements two solutions for the mapping of
+ the virtual output device:
+ The old solution set the origin of the mapping mode, which will be used in
+ the virtual output device. This causes several paint errors, because of the
+ different roundings in the virtual output device and the original output device.
+ The new solution avoids the rounding differences between virtual and original
+ output device by setting a pixel offset at the virtual output device.
+ A define controls, which solution is used, in order to switch in escalation
+ back to old solution.
+
+ @param _pOrgOutDev
+ input parameter - constant instance of the original output device, for which
+ the virtual output device is created.
+
+ @param _pVirDev
+ input/output parameter - instance of the virtual output device.
+
+ @param _rNewOrigin
+ input parameter - constant instance of the origin, which will be used in
+ the virtual output device
+*/
+// define to control, if old or new solution for setting the mapping for
+// a virtual output device is used.
+static void SetMappingForVirtDev( const Point& _rNewOrigin,
+ const vcl::RenderContext* _pOrgOutDev,
+ vcl::RenderContext* _pVirDev )
+{
+ // new solution: set pixel offset at virtual output device
+ Point aPixelOffset = _pOrgOutDev->LogicToPixel( _rNewOrigin );
+ _pVirDev->SetPixelOffset( Size( -aPixelOffset.X(), -aPixelOffset.Y() ) );
+}
+
+// rSize must be pixel coordinates!
+bool SwLayVout::DoesFit( const Size &rNew )
+{
+ if( rNew.Height() > VIRTUALHEIGHT )
+ return false;
+ if( rNew.IsEmpty() )
+ return false;
+ if( rNew.Width() <= m_aSize.Width() )
+ return true;
+ if( !m_pVirDev )
+ {
+ m_pVirDev = VclPtr<VirtualDevice>::Create();
+ m_pVirDev->SetLineColor();
+ if( m_pOut )
+ {
+ if( m_pVirDev->GetFillColor() != m_pOut->GetFillColor() )
+ m_pVirDev->SetFillColor( m_pOut->GetFillColor() );
+ }
+ }
+
+ if( rNew.Width() > m_aSize.Width() )
+ {
+ m_aSize.setWidth( rNew.Width() );
+ if( !m_pVirDev->SetOutputSizePixel( m_aSize ) )
+ {
+ m_pVirDev.disposeAndClear();
+ m_aSize.setWidth( 0 );
+ return false;
+ }
+ }
+ return true;
+}
+
+/// change 2nd parameter <rRect> - no longer <const>
+/// in order to return value of class member variable <aRect>, if virtual
+/// output is used.
+/// <aRect> contains the rectangle that represents the area the virtual
+/// output device is used for and that is flushed at the end.
+void SwLayVout::Enter( SwViewShell *pShell, SwRect &rRect, bool bOn )
+{
+ Flush();
+
+#ifdef DBG_UTIL
+ if( pShell->GetViewOptions()->IsTest3() )
+ {
+ ++m_nCount;
+ return;
+ }
+#endif
+
+ bOn = bOn && !m_nCount && rRect.HasArea() && pShell->GetWin();
+ ++m_nCount;
+ if( !bOn )
+ return;
+
+ m_pShell = pShell;
+ m_pOut = nullptr;
+ OutputDevice *pO = m_pShell->GetOut();
+// We don't cheat on printers or virtual output devices...
+ if( OUTDEV_WINDOW != pO->GetOutDevType() )
+ return;
+
+ m_pOut = pO;
+ Size aPixSz( m_pOut->PixelToLogic( Size( 1,1 )) );
+ SwRect aTmp( rRect );
+ aTmp.AddWidth(aPixSz.Width()/2 + 1 );
+ aTmp.AddHeight(aPixSz.Height()/2 + 1 );
+ tools::Rectangle aTmpRect( pO->LogicToPixel( aTmp.SVRect() ) );
+
+ OSL_ENSURE( !m_pShell->GetWin()->IsReallyVisible() ||
+ aTmpRect.GetWidth() <= m_pShell->GetWin()->GetOutputSizePixel().Width() + 2,
+ "Paintwidth bigger than visarea?" );
+ // Does the rectangle fit in our buffer?
+ if( !DoesFit( aTmpRect.GetSize() ) )
+ {
+ m_pOut = nullptr;
+ return;
+ }
+
+ m_aRect = SwRect( pO->PixelToLogic( aTmpRect ) );
+
+ SetOutDev( m_pShell, m_pVirDev );
+
+ if( m_pVirDev->GetFillColor() != m_pOut->GetFillColor() )
+ m_pVirDev->SetFillColor( m_pOut->GetFillColor() );
+
+ MapMode aMapMode( m_pOut->GetMapMode() );
+ // use method to set mapping
+ //aMapMode.SetOrigin( Point(0,0) - aRect.Pos() );
+ ::SetMappingForVirtDev( m_aRect.Pos(), m_pOut, m_pVirDev );
+
+ if( aMapMode != m_pVirDev->GetMapMode() )
+ m_pVirDev->SetMapMode( aMapMode );
+
+ // set value of parameter <rRect>
+ rRect = m_aRect;
+
+}
+
+void SwLayVout::Flush_()
+{
+ OSL_ENSURE( m_pVirDev, "SwLayVout::DrawOut: nothing left Toulouse" );
+ m_pOut->DrawOutDev( m_aRect.Pos(), m_aRect.SSize(),
+ m_aRect.Pos(), m_aRect.SSize(), *m_pVirDev );
+ SetOutDev( m_pShell, m_pOut );
+ m_pOut = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/virtoutp.hxx b/sw/source/core/layout/virtoutp.hxx
new file mode 100644
index 000000000..df24ec196
--- /dev/null
+++ b/sw/source/core/layout/virtoutp.hxx
@@ -0,0 +1,61 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_VIRTOUTP_HXX
+#define INCLUDED_SW_SOURCE_CORE_LAYOUT_VIRTOUTP_HXX
+
+#include <vcl/virdev.hxx>
+
+#include <swrect.hxx>
+
+class SwViewShell;
+#define VIRTUALHEIGHT 64
+
+class SwLayVout
+{
+ friend void FrameFinit(); //deletes Vout
+private:
+ SwViewShell* m_pShell;
+ VclPtr<OutputDevice> m_pOut;
+ VclPtr<VirtualDevice> m_pVirDev;
+ SwRect m_aRect;
+ SwRect m_aOrgRect;
+ Size m_aSize;
+ sal_uInt16 m_nCount;
+
+ bool DoesFit( const Size &rOut );
+
+public:
+ SwLayVout() : m_pShell(nullptr), m_pOut(nullptr), m_pVirDev(nullptr), m_aSize(0, VIRTUALHEIGHT), m_nCount(0) {}
+ ~SwLayVout() { m_pVirDev.disposeAndClear(); }
+
+ /// OD 27.09.2002 #103636# - change 2nd parameter <rRect> - no longer <const>
+ void Enter( SwViewShell *pShell, SwRect &rRect, bool bOn );
+ void Leave() { --m_nCount; Flush(); }
+
+ void SetOrgRect( SwRect const &rRect ) { m_aOrgRect = rRect; }
+ const SwRect& GetOrgRect() const { return m_aOrgRect; }
+
+ bool IsFlushable() const { return bool(m_pOut); }
+ void Flush_();
+ void Flush() { if( m_pOut ) Flush_(); }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx
new file mode 100644
index 000000000..1eadc9db6
--- /dev/null
+++ b/sw/source/core/layout/wsfrm.cxx
@@ -0,0 +1,4743 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <hints.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/safeint.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/brushitem.hxx>
+#include <fmtornt.hxx>
+#include <pagefrm.hxx>
+#include <section.hxx>
+#include <rootfrm.hxx>
+#include <anchoreddrawobject.hxx>
+#include <fmtanchr.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <redline.hxx>
+#include <docsh.hxx>
+#include <ftninfo.hxx>
+#include <ftnidx.hxx>
+#include <fmtclbl.hxx>
+#include <fmtfsize.hxx>
+#include <fmtpdsc.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtcntnt.hxx>
+#include <ftnfrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <flyfrm.hxx>
+#include <sectfrm.hxx>
+#include <fmtclds.hxx>
+#include <txtfrm.hxx>
+#include <bodyfrm.hxx>
+#include <cellfrm.hxx>
+#include <dbg_lay.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <sortedobjs.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <layact.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+
+// RotateFlyFrame3
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+using namespace ::com::sun::star;
+
+SwFrameAreaDefinition::SwFrameAreaDefinition()
+: mbFrameAreaPositionValid(false),
+ mbFrameAreaSizeValid(false),
+ mbFramePrintAreaValid(false),
+ mnFrameId(SwFrameAreaDefinition::snLastFrameId++)
+{
+}
+
+SwFrameAreaDefinition::~SwFrameAreaDefinition()
+{
+}
+
+void SwFrameAreaDefinition::setFrameAreaPositionValid(bool bNew)
+{
+ if(mbFrameAreaPositionValid != bNew)
+ {
+ mbFrameAreaPositionValid = bNew;
+ }
+}
+
+void SwFrameAreaDefinition::setFrameAreaSizeValid(bool bNew)
+{
+ if(mbFrameAreaSizeValid != bNew)
+ {
+ mbFrameAreaSizeValid = bNew;
+ }
+}
+
+void SwFrameAreaDefinition::setFramePrintAreaValid(bool bNew)
+{
+ if(mbFramePrintAreaValid != bNew)
+ {
+ mbFramePrintAreaValid = bNew;
+ }
+}
+
+SwFrameAreaDefinition::FrameAreaWriteAccess::~FrameAreaWriteAccess()
+{
+ if(mrTarget.maFrameArea != *this)
+ {
+ mrTarget.maFrameArea = *this;
+ }
+}
+
+SwFrameAreaDefinition::FramePrintAreaWriteAccess::~FramePrintAreaWriteAccess()
+{
+ if(mrTarget.maFramePrintArea != *this)
+ {
+ mrTarget.maFramePrintArea = *this;
+ }
+}
+
+// RotateFlyFrame3 - Support for Transformations
+basegfx::B2DHomMatrix SwFrameAreaDefinition::getFrameAreaTransformation() const
+{
+ // default implementation hands out FrameArea (outer frame)
+ const SwRect& rFrameArea(getFrameArea());
+
+ return basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rFrameArea.Width(), rFrameArea.Height(),
+ rFrameArea.Left(), rFrameArea.Top());
+}
+
+basegfx::B2DHomMatrix SwFrameAreaDefinition::getFramePrintAreaTransformation() const
+{
+ // default implementation hands out FramePrintArea (outer frame)
+ // Take into account that FramePrintArea is relative to FrameArea
+ const SwRect& rFrameArea(getFrameArea());
+ const SwRect& rFramePrintArea(getFramePrintArea());
+
+ return basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rFramePrintArea.Width(), rFramePrintArea.Height(),
+ rFramePrintArea.Left() + rFrameArea.Left(),
+ rFramePrintArea.Top() + rFrameArea.Top());
+}
+
+void SwFrameAreaDefinition::transform_translate(const Point& rOffset)
+{
+ // RotateFlyFrame3: default is to change the FrameArea, FramePrintArea needs no
+ // change since it is relative to FrameArea
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if (aFrm.Pos().X() != FAR_AWAY)
+ {
+ aFrm.Pos().AdjustX(rOffset.X() );
+ }
+
+ if (aFrm.Pos().Y() != FAR_AWAY)
+ {
+ aFrm.Pos().AdjustY(rOffset.Y() );
+ }
+}
+
+SwRect TransformableSwFrame::getUntransformedFrameArea() const
+{
+ const basegfx::B2DHomMatrix& rSource(getLocalFrameAreaTransformation());
+
+ if(rSource.isIdentity())
+ {
+ return mrSwFrameAreaDefinition.getFrameArea();
+ }
+ else
+ {
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSource.decompose(aScale, aTranslate, fRotate, fShearX);
+ const basegfx::B2DPoint aCenter(rSource * basegfx::B2DPoint(0.5, 0.5));
+ const basegfx::B2DVector aAbsScale(basegfx::absolute(aScale));
+
+ return SwRect(
+ basegfx::fround(aCenter.getX() - (0.5 * aAbsScale.getX())),
+ basegfx::fround(aCenter.getY() - (0.5 * aAbsScale.getY())),
+ basegfx::fround(aAbsScale.getX()),
+ basegfx::fround(aAbsScale.getY()));
+ }
+}
+
+SwRect TransformableSwFrame::getUntransformedFramePrintArea() const
+{
+ const basegfx::B2DHomMatrix& rSource(getLocalFramePrintAreaTransformation());
+
+ if(rSource.isIdentity())
+ {
+ return mrSwFrameAreaDefinition.getFramePrintArea();
+ }
+ else
+ {
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSource.decompose(aScale, aTranslate, fRotate, fShearX);
+ const basegfx::B2DPoint aCenter(rSource * basegfx::B2DPoint(0.5, 0.5));
+ const basegfx::B2DVector aAbsScale(basegfx::absolute(aScale));
+ const SwRect aUntransformedFrameArea(getUntransformedFrameArea());
+
+ return SwRect(
+ basegfx::fround(aCenter.getX() - (0.5 * aAbsScale.getX())) - aUntransformedFrameArea.Left(),
+ basegfx::fround(aCenter.getY() - (0.5 * aAbsScale.getY())) - aUntransformedFrameArea.Top(),
+ basegfx::fround(aAbsScale.getX()),
+ basegfx::fround(aAbsScale.getY()));
+ }
+}
+
+void TransformableSwFrame::createFrameAreaTransformations(
+ double fRotation,
+ const basegfx::B2DPoint& rCenter)
+{
+ const basegfx::B2DHomMatrix aRotateAroundCenter(
+ basegfx::utils::createRotateAroundPoint(
+ rCenter.getX(),
+ rCenter.getY(),
+ fRotation));
+ const SwRect& rFrameArea(mrSwFrameAreaDefinition.getFrameArea());
+ const SwRect& rFramePrintArea(mrSwFrameAreaDefinition.getFramePrintArea());
+
+ maFrameAreaTransformation = aRotateAroundCenter * basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rFrameArea.Width(), rFrameArea.Height(),
+ rFrameArea.Left(), rFrameArea.Top());
+ maFramePrintAreaTransformation = aRotateAroundCenter * basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rFramePrintArea.Width(), rFramePrintArea.Height(),
+ rFramePrintArea.Left() + rFrameArea.Left(), rFramePrintArea.Top() + rFrameArea.Top());
+}
+
+void TransformableSwFrame::adaptFrameAreasToTransformations()
+{
+ if(!getLocalFrameAreaTransformation().isIdentity())
+ {
+ basegfx::B2DRange aRangeFrameArea(0.0, 0.0, 1.0, 1.0);
+ aRangeFrameArea.transform(getLocalFrameAreaTransformation());
+ const SwRect aNewFrm(
+ basegfx::fround(aRangeFrameArea.getMinX()), basegfx::fround(aRangeFrameArea.getMinY()),
+ basegfx::fround(aRangeFrameArea.getWidth()), basegfx::fround(aRangeFrameArea.getHeight()));
+
+ if(aNewFrm != mrSwFrameAreaDefinition.getFrameArea())
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(mrSwFrameAreaDefinition);
+ aFrm.setSwRect(aNewFrm);
+ }
+ }
+
+ if(getLocalFramePrintAreaTransformation().isIdentity())
+ return;
+
+ basegfx::B2DRange aRangeFramePrintArea(0.0, 0.0, 1.0, 1.0);
+ aRangeFramePrintArea.transform(getLocalFramePrintAreaTransformation());
+ const SwRect aNewPrt(
+ basegfx::fround(aRangeFramePrintArea.getMinX()) - mrSwFrameAreaDefinition.getFrameArea().Left(),
+ basegfx::fround(aRangeFramePrintArea.getMinY()) - mrSwFrameAreaDefinition.getFrameArea().Top(),
+ basegfx::fround(aRangeFramePrintArea.getWidth()),
+ basegfx::fround(aRangeFramePrintArea.getHeight()));
+
+ if(aNewPrt != mrSwFrameAreaDefinition.getFramePrintArea())
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(mrSwFrameAreaDefinition);
+ aPrt.setSwRect(aNewPrt);
+ }
+}
+
+void TransformableSwFrame::restoreFrameAreas()
+{
+ // This can be done fully based on the Transformations currently
+ // set, so use this. Only needed when transformation *is* used
+ if(!getLocalFrameAreaTransformation().isIdentity())
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(mrSwFrameAreaDefinition);
+ aFrm.setSwRect(getUntransformedFrameArea());
+ }
+
+ if(!getLocalFramePrintAreaTransformation().isIdentity())
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(mrSwFrameAreaDefinition);
+ aPrt.setSwRect(getUntransformedFramePrintArea());
+ }
+}
+
+// transform by given B2DHomMatrix
+void TransformableSwFrame::transform(const basegfx::B2DHomMatrix& aTransform)
+{
+ maFrameAreaTransformation *= aTransform;
+ maFramePrintAreaTransformation *= aTransform;
+}
+
+SwFrame::SwFrame( sw::BroadcastingModify *pMod, SwFrame* pSib )
+: SwClient( pMod ),
+ mpRoot( pSib ? pSib->getRootFrame() : nullptr ),
+ mpUpper(nullptr),
+ mpNext(nullptr),
+ mpPrev(nullptr),
+ mnFrameType(SwFrameType::None),
+ mbInDtor(false),
+ mbInvalidR2L(true),
+ mbDerivedR2L(false),
+ mbRightToLeft(false),
+ mbInvalidVert(true),
+ mbDerivedVert(false),
+ mbVertical(false),
+ mbVertLR(false),
+ mbVertLRBT(false),
+ mbValidLineNum(false),
+ mbFixSize(false),
+ mbCompletePaint(true),
+ mbRetouche(false),
+ mbInfInvalid(true),
+ mbInfBody( false ),
+ mbInfTab ( false ),
+ mbInfFly ( false ),
+ mbInfFootnote ( false ),
+ mbInfSct ( false ),
+ mbColLocked(false),
+ m_isInDestroy(false),
+ mnForbidDelete(0)
+{
+ OSL_ENSURE( pMod, "No frame format given." );
+}
+
+const IDocumentDrawModelAccess& SwFrame::getIDocumentDrawModelAccess()
+{
+ return GetUpper()->GetFormat()->getIDocumentDrawModelAccess();
+}
+
+bool SwFrame::KnowsFormat( const SwFormat& rFormat ) const
+{
+ return GetRegisteredIn() == &rFormat;
+}
+
+void SwFrame::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add( this );
+}
+
+void SwFrame::CheckDir( SvxFrameDirection nDir, bool bVert, bool bOnlyBiDi, bool bBrowse )
+{
+ if( SvxFrameDirection::Environment == nDir || ( bVert && bOnlyBiDi ) )
+ {
+ mbDerivedVert = true;
+ if( SvxFrameDirection::Environment == nDir )
+ mbDerivedR2L = true;
+ SetDirFlags( bVert );
+ }
+ else if( bVert )
+ {
+ mbInvalidVert = false;
+ if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir
+ || bBrowse )
+ {
+ mbVertical = false;
+ mbVertLR = false;
+ mbVertLRBT = false;
+ }
+ else
+ {
+ mbVertical = true;
+ if(SvxFrameDirection::Vertical_RL_TB == nDir)
+ {
+ mbVertLR = false;
+ mbVertLRBT = false;
+ }
+ else if(SvxFrameDirection::Vertical_LR_TB==nDir)
+ {
+ mbVertLR = true;
+ mbVertLRBT = false;
+ }
+ else if (nDir == SvxFrameDirection::Vertical_LR_BT)
+ {
+ mbVertLR = true;
+ mbVertLRBT = true;
+ }
+ }
+ }
+ else
+ {
+ mbInvalidR2L = false;
+ if( SvxFrameDirection::Horizontal_RL_TB == nDir )
+ mbRightToLeft = true;
+ else
+ mbRightToLeft = false;
+ }
+}
+
+void SwFrame::CheckDirection( bool bVert )
+{
+ if( bVert )
+ {
+ if( !IsHeaderFrame() && !IsFooterFrame() )
+ {
+ mbDerivedVert = true;
+ SetDirFlags( bVert );
+ }
+ }
+ else
+ {
+ mbDerivedR2L = true;
+ SetDirFlags( bVert );
+ }
+}
+
+void SwSectionFrame::CheckDirection( bool bVert )
+{
+ const SwFrameFormat* pFormat = GetFormat();
+ if( pFormat )
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ CheckDir(pFormat->GetFormatAttr(RES_FRAMEDIR).GetValue(),
+ bVert, true, bBrowseMode );
+ }
+ else
+ SwFrame::CheckDirection( bVert );
+}
+
+void SwFlyFrame::CheckDirection( bool bVert )
+{
+ const SwFrameFormat* pFormat = GetFormat();
+ if( pFormat )
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ CheckDir(pFormat->GetFormatAttr(RES_FRAMEDIR).GetValue(),
+ bVert, false, bBrowseMode );
+ }
+ else
+ SwFrame::CheckDirection( bVert );
+}
+
+void SwTabFrame::CheckDirection( bool bVert )
+{
+ const SwFrameFormat* pFormat = GetFormat();
+ if( pFormat )
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ CheckDir(pFormat->GetFormatAttr(RES_FRAMEDIR).GetValue(),
+ bVert, true, bBrowseMode );
+ }
+ else
+ SwFrame::CheckDirection( bVert );
+}
+
+void SwCellFrame::CheckDirection( bool bVert )
+{
+ const SwFrameFormat* pFormat = GetFormat();
+ const SvxFrameDirectionItem* pFrameDirItem;
+ // Check if the item is set, before actually
+ // using it. Otherwise the dynamic pool default is used, which may be set
+ // to LTR in case of OOo 1.0 documents.
+ if( pFormat && (pFrameDirItem = pFormat->GetItemIfSet( RES_FRAMEDIR ) ) )
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ CheckDir( pFrameDirItem->GetValue(), bVert, false, bBrowseMode );
+ }
+ else
+ SwFrame::CheckDirection( bVert );
+}
+
+void SwTextFrame::CheckDirection( bool bVert )
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ CheckDir(GetTextNodeForParaProps()->GetSwAttrSet().GetFrameDir().GetValue(),
+ bVert, true, bBrowseMode);
+}
+
+void SwFrame::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SwFrameInvFlags eInvFlags = SwFrameInvFlags::NONE;
+
+ if(pLegacy->m_pOld && pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
+ {
+ SfxItemIter aNIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet());
+ SfxItemIter aOIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet());
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ do
+ {
+ UpdateAttrFrame(pOItem, pNItem, eInvFlags);
+ pNItem = aNIter.NextItem();
+ pOItem = aOIter.NextItem();
+ } while (pNItem);
+ }
+ else
+ UpdateAttrFrame(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if(eInvFlags == SwFrameInvFlags::NONE)
+ return;
+
+ SwPageFrame* pPage = FindPageFrame();
+ InvalidatePage(pPage);
+ if(eInvFlags & SwFrameInvFlags::InvalidatePrt)
+ {
+ InvalidatePrt_();
+ if(!GetPrev() && IsTabFrame() && IsInSct())
+ FindSctFrame()->InvalidatePrt_();
+ }
+ if(eInvFlags & SwFrameInvFlags::InvalidateSize)
+ InvalidateSize_();
+ if(eInvFlags & SwFrameInvFlags::InvalidatePos)
+ InvalidatePos_();
+ if(eInvFlags & SwFrameInvFlags::SetCompletePaint)
+ SetCompletePaint();
+ SwFrame *pNxt;
+ if (eInvFlags & (SwFrameInvFlags::NextInvalidatePos | SwFrameInvFlags::NextSetCompletePaint)
+ && nullptr != (pNxt = GetNext()))
+ {
+ pNxt->InvalidatePage(pPage);
+ if(eInvFlags & SwFrameInvFlags::NextInvalidatePos)
+ pNxt->InvalidatePos_();
+ if(eInvFlags & SwFrameInvFlags::NextSetCompletePaint)
+ pNxt->SetCompletePaint();
+ }
+}
+
+void SwFrame::UpdateAttrFrame( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
+ SwFrameInvFlags &rInvFlags )
+{
+ sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ switch( nWhich )
+ {
+ case RES_BOX:
+ case RES_SHADOW:
+ Prepare( PrepareHint::FixSizeChanged );
+ [[fallthrough]];
+ case RES_LR_SPACE:
+ case RES_UL_SPACE:
+ case RES_RTL_GUTTER:
+ rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize
+ | SwFrameInvFlags::SetCompletePaint;
+ break;
+
+ case RES_HEADER_FOOTER_EAT_SPACING:
+ rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize;
+ break;
+
+ case RES_BACKGROUND:
+ case RES_BACKGROUND_FULL_SIZE:
+ rInvFlags |= SwFrameInvFlags::SetCompletePaint | SwFrameInvFlags::NextSetCompletePaint;
+ break;
+
+ case RES_KEEP:
+ rInvFlags |= SwFrameInvFlags::InvalidatePos;
+ break;
+
+ case RES_FRM_SIZE:
+ ReinitializeFrameSizeAttrFlags();
+ rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize
+ | SwFrameInvFlags::NextInvalidatePos;
+ break;
+
+ case RES_FMT_CHG:
+ rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize
+ | SwFrameInvFlags::InvalidatePos | SwFrameInvFlags::SetCompletePaint;
+ break;
+
+ case RES_ROW_SPLIT:
+ {
+ if ( IsRowFrame() )
+ {
+ bool bInFollowFlowRow = nullptr != IsInFollowFlowRow();
+ if ( bInFollowFlowRow || nullptr != IsInSplitTableRow() )
+ {
+ SwTabFrame* pTab = FindTabFrame();
+ if ( bInFollowFlowRow )
+ pTab = pTab->FindMaster();
+ pTab->SetRemoveFollowFlowLinePending( true );
+ }
+ }
+ break;
+ }
+ case RES_COL:
+ OSL_FAIL( "Columns for new FrameType?" );
+ break;
+
+ default:
+ // the new FillStyle has to do the same as previous RES_BACKGROUND
+ if(nWhich >= XATTR_FILL_FIRST && nWhich <= XATTR_FILL_LAST)
+ {
+ rInvFlags
+ |= SwFrameInvFlags::SetCompletePaint | SwFrameInvFlags::NextSetCompletePaint;
+ }
+ /* do Nothing */;
+ }
+}
+
+bool SwFrame::Prepare( const PrepareHint, const void *, bool )
+{
+ /* Do nothing */
+ return false;
+}
+
+/**
+ * Invalidates the page in which the Frame is currently placed.
+ * The page is invalidated depending on the type (Layout, Content, FlyFrame)
+ */
+void SwFrame::InvalidatePage( const SwPageFrame *pPage ) const
+{
+ if ( !pPage )
+ {
+ pPage = FindPageFrame();
+ // #i28701# - for at-character and as-character
+ // anchored Writer fly frames additionally invalidate also page frame
+ // its 'anchor character' is on.
+ if ( pPage && pPage->GetUpper() && IsFlyFrame() )
+ {
+ const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(this);
+ if ( pFlyFrame->IsAutoPos() || pFlyFrame->IsFlyInContentFrame() )
+ {
+ // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
+ // is replaced by method <FindPageFrameOfAnchor()>. It's return value
+ // have to be checked.
+ SwPageFrame* pPageFrameOfAnchor =
+ const_cast<SwFlyFrame*>(pFlyFrame)->FindPageFrameOfAnchor();
+ if ( pPageFrameOfAnchor && pPageFrameOfAnchor != pPage )
+ {
+ InvalidatePage( pPageFrameOfAnchor );
+ }
+ }
+ }
+ }
+
+ if ( !(pPage && pPage->GetUpper()) )
+ return;
+
+ if ( pPage->GetFormat()->GetDoc()->IsInDtor() )
+ return;
+
+ SwRootFrame *pRoot = const_cast<SwRootFrame*>(static_cast<const SwRootFrame*>(pPage->GetUpper()));
+ const SwFlyFrame *pFly = FindFlyFrame();
+ if ( IsContentFrame() )
+ {
+ if ( pRoot->IsTurboAllowed() )
+ {
+ // If a ContentFrame wants to register for a second time, make it a TurboAction.
+ if ( !pRoot->GetTurbo() || this == pRoot->GetTurbo() )
+ pRoot->SetTurbo( static_cast<const SwContentFrame*>(this) );
+ else
+ {
+ pRoot->DisallowTurbo();
+ //The page of the Turbo could be a different one then mine,
+ //therefore we have to invalidate it.
+ const SwFrame *pTmp = pRoot->GetTurbo();
+ pRoot->ResetTurbo();
+ pTmp->InvalidatePage();
+ }
+ }
+ if ( !pRoot->GetTurbo() )
+ {
+ if ( pFly )
+ { if( !pFly->IsLocked() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ { pPage->InvalidateFlyInCnt();
+ pFly->GetAnchorFrame()->InvalidatePage();
+ }
+ else
+ pPage->InvalidateFlyContent();
+ }
+ }
+ else
+ pPage->InvalidateContent();
+ }
+ }
+ else
+ {
+ pRoot->DisallowTurbo();
+ if ( pFly )
+ {
+ if ( !pFly->IsLocked() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ pPage->InvalidateFlyInCnt();
+ pFly->GetAnchorFrame()->InvalidatePage();
+ }
+ else
+ pPage->InvalidateFlyLayout();
+ }
+ }
+ else
+ pPage->InvalidateLayout();
+
+ if ( pRoot->GetTurbo() )
+ { const SwFrame *pTmp = pRoot->GetTurbo();
+ pRoot->ResetTurbo();
+ pTmp->InvalidatePage();
+ }
+ }
+ pRoot->SetIdleFlags();
+
+ if (!IsTextFrame())
+ return;
+
+ SwTextFrame const*const pText(static_cast<SwTextFrame const*>(this));
+ if (sw::MergedPara const*const pMergedPara = pText->GetMergedPara())
+ {
+ SwTextNode const* pNode(nullptr);
+ for (auto const& e : pMergedPara->extents)
+ {
+ if (e.pNode != pNode)
+ {
+ pNode = e.pNode;
+ if (pNode->IsGrammarCheckDirty())
+ {
+ pRoot->SetNeedGrammarCheck( true );
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (pText->GetTextNodeFirst()->IsGrammarCheckDirty())
+ {
+ pRoot->SetNeedGrammarCheck( true );
+ }
+ }
+}
+
+Size SwFrame::ChgSize( const Size& aNewSize )
+{
+ mbFixSize = true;
+ const Size aOldSize( getFrameArea().SSize() );
+ if ( aNewSize == aOldSize )
+ return aOldSize;
+
+ if ( GetUpper() )
+ {
+ bool bNeighb = IsNeighbourFrame();
+ SwRectFn fnRect = IsVertical() == bNeighb ? fnRectHori : ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert );
+ SwRect aNew( Point(0,0), aNewSize );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ (aFrm.*fnRect->fnSetWidth)( (aNew.*fnRect->fnGetWidth)() );
+ }
+
+ tools::Long nNew = (aNew.*fnRect->fnGetHeight)();
+ tools::Long nDiff = nNew - (getFrameArea().*fnRect->fnGetHeight)();
+
+ if( nDiff )
+ {
+ if ( GetUpper()->IsFootnoteBossFrame() && HasFixSize() &&
+ SwNeighbourAdjust::GrowShrink !=
+ static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment() )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ (aFrm.*fnRect->fnSetHeight)( nNew );
+ }
+
+ SwTwips nReal = static_cast<SwLayoutFrame*>(this)->AdjustNeighbourhood(nDiff);
+
+ if ( nReal != nDiff )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ (aFrm.*fnRect->fnSetHeight)( nNew - nDiff + nReal );
+ }
+ }
+ else
+ {
+ // OD 24.10.2002 #97265# - grow/shrink not for neighbour frames
+ // NOTE: neighbour frames are cell and column frames.
+ if ( !bNeighb )
+ {
+ if ( nDiff > 0 )
+ Grow( nDiff );
+ else
+ Shrink( -nDiff );
+
+ if ( GetUpper() && (getFrameArea().*fnRect->fnGetHeight)() != nNew )
+ {
+ GetUpper()->InvalidateSize_();
+ }
+ }
+
+ // Even if grow/shrink did not yet set the desired width, for
+ // example when called by ChgColumns to set the column width, we
+ // set the right width now.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ (aFrm.*fnRect->fnSetHeight)( nNew );
+ }
+ }
+ }
+ else
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.SSize( aNewSize );
+ }
+
+ if ( getFrameArea().SSize() != aOldSize )
+ {
+ SwPageFrame *pPage = FindPageFrame();
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos_();
+ GetNext()->InvalidatePage( pPage );
+ }
+ if( IsLayoutFrame() )
+ {
+ if( IsRightToLeft() )
+ InvalidatePos_();
+ if( static_cast<SwLayoutFrame*>(this)->Lower() )
+ static_cast<SwLayoutFrame*>(this)->Lower()->InvalidateSize_();
+ }
+ InvalidatePrt_();
+ InvalidateSize_();
+ InvalidatePage( pPage );
+ }
+
+ return getFrameArea().SSize();
+}
+
+/** Insert SwFrame into existing structure.
+ *
+ * Insertion is done below the parent either before pBehind or
+ * at the end of the chain if pBehind is empty.
+ */
+void SwFrame::InsertBefore( SwLayoutFrame* pParent, SwFrame* pBehind )
+{
+ OSL_ENSURE( pParent, "No parent for insert." );
+ OSL_ENSURE( (!pBehind || pParent == pBehind->GetUpper()),
+ "Frame tree is inconsistent." );
+
+ mpUpper = pParent;
+ mpNext = pBehind;
+ if( pBehind )
+ { //Insert before pBehind.
+ mpPrev = pBehind->mpPrev;
+ if( nullptr != mpPrev )
+ mpPrev->mpNext = this;
+ else
+ mpUpper->m_pLower = this;
+ pBehind->mpPrev = this;
+ }
+ else
+ { //Insert at the end, or as first node in the sub tree
+ mpPrev = mpUpper->Lower();
+ if ( mpPrev )
+ {
+ while( mpPrev->mpNext )
+ mpPrev = mpPrev->mpNext;
+ mpPrev->mpNext = this;
+ }
+ else
+ mpUpper->m_pLower = this;
+ }
+}
+
+/** Insert SwFrame into existing structure.
+ *
+ * Insertion is done below the parent either after pBehind or
+ * at the beginning of the chain if pBehind is empty.
+ */
+void SwFrame::InsertBehind( SwLayoutFrame *pParent, SwFrame *pBefore )
+{
+ OSL_ENSURE( pParent, "No Parent for Insert." );
+ OSL_ENSURE( (!pBefore || pParent == pBefore->GetUpper()),
+ "Frame tree is inconsistent." );
+
+ mpUpper = pParent;
+ mpPrev = pBefore;
+ if ( pBefore )
+ {
+ //Insert after pBefore
+ mpNext = pBefore->mpNext;
+ if ( nullptr != mpNext )
+ mpNext->mpPrev = this;
+ pBefore->mpNext = this;
+ }
+ else
+ {
+ //Insert at the beginning of the chain
+ mpNext = pParent->Lower();
+ if ( pParent->Lower() )
+ pParent->Lower()->mpPrev = this;
+ pParent->m_pLower = this;
+ }
+}
+
+/** Insert a chain of SwFrames into an existing structure
+ *
+ * Currently, this method is used to insert a SectionFrame (which may have some siblings) into an
+ * existing structure. If the third parameter is NULL, this method is (besides handling the
+ * siblings) equal to SwFrame::InsertBefore(..).
+ *
+ * If the third parameter is passed, the following happens:
+ * - this becomes mpNext of pParent
+ * - pSct becomes mpNext of the last one in the this-chain
+ * - pBehind is reconnected from pParent to pSct
+ * The purpose is: a SectionFrame (this) won't become a child of another SectionFrame (pParent), but
+ * pParent gets split into two siblings (pParent+pSect) and this is inserted between.
+ */
+bool SwFrame::InsertGroupBefore( SwFrame* pParent, SwFrame* pBehind, SwFrame* pSct )
+{
+ OSL_ENSURE( pParent, "No parent for insert." );
+ OSL_ENSURE( (!pBehind || ( (pBehind && (pParent == pBehind->GetUpper()))
+ || ((pParent->IsSctFrame() && pBehind->GetUpper()->IsColBodyFrame())) ) ),
+ "Frame tree inconsistent." );
+ if( pSct )
+ {
+ mpUpper = pParent->GetUpper();
+ SwFrame *pLast = this;
+ while( pLast->GetNext() )
+ {
+ pLast = pLast->GetNext();
+ pLast->mpUpper = GetUpper();
+ }
+ if( pBehind )
+ {
+ pLast->mpNext = pSct;
+ pSct->mpPrev = pLast;
+ pSct->mpNext = pParent->GetNext();
+ }
+ else
+ {
+ pLast->mpNext = pParent->GetNext();
+ if( pLast->GetNext() )
+ pLast->GetNext()->mpPrev = pLast;
+ }
+ pParent->mpNext = this;
+ mpPrev = pParent;
+ if( pSct->GetNext() )
+ pSct->GetNext()->mpPrev = pSct;
+ while( pLast->GetNext() )
+ {
+ pLast = pLast->GetNext();
+ pLast->mpUpper = GetUpper();
+ }
+ if( pBehind )
+ { // Insert before pBehind.
+ if( pBehind->GetPrev() )
+ pBehind->GetPrev()->mpNext = nullptr;
+ else
+ pBehind->GetUpper()->m_pLower = nullptr;
+ pBehind->mpPrev = nullptr;
+ SwLayoutFrame* pTmp = static_cast<SwLayoutFrame*>(pSct);
+ if( pTmp->Lower() )
+ {
+ OSL_ENSURE( pTmp->Lower()->IsColumnFrame(), "InsertGrp: Used SectionFrame" );
+ pTmp = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pTmp->Lower())->Lower());
+ OSL_ENSURE( pTmp, "InsertGrp: Missing ColBody" );
+ }
+ pBehind->mpUpper = pTmp;
+ pBehind->GetUpper()->m_pLower = pBehind;
+ pLast = pBehind->GetNext();
+ while ( pLast )
+ {
+ pLast->mpUpper = pBehind->GetUpper();
+ pLast = pLast->GetNext();
+ }
+ }
+ else
+ {
+ OSL_ENSURE( pSct->IsSctFrame(), "InsertGroup: For SectionFrames only" );
+ SwFrame::DestroyFrame(pSct);
+ return false;
+ }
+ }
+ else
+ {
+ mpUpper = static_cast<SwLayoutFrame*>(pParent);
+ SwFrame *pLast = this;
+ while( pLast->GetNext() )
+ {
+ pLast = pLast->GetNext();
+ pLast->mpUpper = GetUpper();
+ }
+ pLast->mpNext = pBehind;
+ if( pBehind )
+ { // Insert before pBehind.
+ mpPrev = pBehind->mpPrev;
+ if( nullptr != mpPrev )
+ mpPrev->mpNext = this;
+ else
+ mpUpper->m_pLower = this;
+ pBehind->mpPrev = pLast;
+ }
+ else
+ {
+ //Insert at the end, or ... the first node in the subtree
+ mpPrev = mpUpper->Lower();
+ if ( mpPrev )
+ {
+ while( mpPrev->mpNext )
+ mpPrev = mpPrev->mpNext;
+ mpPrev->mpNext = this;
+ }
+ else
+ mpUpper->m_pLower = this;
+ }
+ }
+ return true;
+}
+
+void SwFrame::RemoveFromLayout()
+{
+ OSL_ENSURE( mpUpper, "Remove without upper?" );
+
+ if (mpPrev)
+ // one out of the middle is removed
+ mpPrev->mpNext = mpNext;
+ else if (mpUpper)
+ { // the first in a list is removed //TODO
+ OSL_ENSURE( mpUpper->m_pLower == this, "Layout is inconsistent." );
+ mpUpper->m_pLower = mpNext;
+ }
+ if( mpNext )
+ mpNext->mpPrev = mpPrev;
+
+ // Remove link
+ mpNext = mpPrev = nullptr;
+ mpUpper = nullptr;
+}
+
+void SwContentFrame::Paste( SwFrame* pParent, SwFrame* pSibling)
+{
+ OSL_ENSURE( pParent, "No parent for pasting." );
+ OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
+ OSL_ENSURE( pParent != this, "I'm the parent." );
+ OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
+ OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
+ "I'm still registered somewhere" );
+ OSL_ENSURE( !pSibling || pSibling->IsFlowFrame(),
+ "<SwContentFrame::Paste(..)> - sibling not of expected type." );
+
+ //Insert in the tree.
+ InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
+
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidateAll_();
+ InvalidatePage( pPage );
+
+ if( pPage )
+ {
+ pPage->InvalidateSpelling();
+ pPage->InvalidateSmartTags();
+ pPage->InvalidateAutoCompleteWords();
+ pPage->InvalidateWordCount();
+ }
+
+ if ( GetNext() )
+ {
+ SwFrame* pNxt = GetNext();
+ pNxt->InvalidatePrt_();
+ pNxt->InvalidatePos_();
+ pNxt->InvalidatePage( pPage );
+ if( pNxt->IsSctFrame() )
+ pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsContent();
+ if( pNxt && pNxt->IsTextFrame() && pNxt->IsInFootnote() )
+ pNxt->Prepare( PrepareHint::FootnoteInvalidation, nullptr, false );
+ }
+
+ if ( getFrameArea().Height() )
+ pParent->Grow( getFrameArea().Height() );
+
+ if ( getFrameArea().Width() != pParent->getFramePrintArea().Width() )
+ Prepare( PrepareHint::FixSizeChanged );
+
+ if ( GetPrev() )
+ {
+ if ( IsFollow() )
+ //I'm a direct follower of my master now
+ static_cast<SwContentFrame*>(GetPrev())->Prepare( PrepareHint::FollowFollows );
+ else
+ {
+ if ( GetPrev()->getFrameArea().Height() !=
+ GetPrev()->getFramePrintArea().Height() + GetPrev()->getFramePrintArea().Top() )
+ {
+ // Take the border into account?
+ GetPrev()->InvalidatePrt_();
+ }
+ // OD 18.02.2003 #104989# - force complete paint of previous frame,
+ // if frame is inserted at the end of a section frame, in order to
+ // get subsidiary lines repainted for the section.
+ if ( pParent->IsSctFrame() && !GetNext() )
+ {
+ // force complete paint of previous frame, if new inserted frame
+ // in the section is the last one.
+ GetPrev()->SetCompletePaint();
+ }
+ GetPrev()->InvalidatePage( pPage );
+ }
+ }
+ if ( IsInFootnote() )
+ {
+ SwFrame* pFrame = GetIndPrev();
+ if( pFrame && pFrame->IsSctFrame() )
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pFrame )
+ pFrame->Prepare( PrepareHint::QuoVadis, nullptr, false );
+ if( !GetNext() )
+ {
+ pFrame = FindFootnoteFrame()->GetNext();
+ if( pFrame && nullptr != (pFrame=static_cast<SwLayoutFrame*>(pFrame)->ContainsAny()) )
+ pFrame->InvalidatePrt_();
+ }
+ }
+
+ InvalidateLineNum_();
+ SwFrame *pNxt = FindNextCnt();
+ if ( !pNxt )
+ return;
+
+ while ( pNxt && pNxt->IsInTab() )
+ {
+ pNxt = pNxt->FindTabFrame();
+ if( nullptr != pNxt )
+ pNxt = pNxt->FindNextCnt();
+ }
+ if ( pNxt )
+ {
+ pNxt->InvalidateLineNum_();
+ if ( pNxt != GetNext() )
+ pNxt->InvalidatePage();
+ }
+}
+
+void SwContentFrame::Cut()
+{
+ OSL_ENSURE( GetUpper(), "Cut without Upper()." );
+
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+ SwFrame *pFrame = GetIndPrev();
+ if( pFrame )
+ {
+ if( pFrame->IsSctFrame() )
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if ( pFrame && pFrame->IsContentFrame() )
+ {
+ pFrame->InvalidatePrt_();
+ if( IsInFootnote() )
+ pFrame->Prepare( PrepareHint::QuoVadis, nullptr, false );
+ }
+ // #i26250# - invalidate printing area of previous
+ // table frame.
+ else if ( pFrame && pFrame->IsTabFrame() )
+ {
+ pFrame->InvalidatePrt();
+ }
+ }
+
+ SwFrame *pNxt = FindNextCnt();
+ if ( pNxt )
+ {
+ while ( pNxt && pNxt->IsInTab() )
+ {
+ pNxt = pNxt->FindTabFrame();
+ if( nullptr != pNxt )
+ pNxt = pNxt->FindNextCnt();
+ }
+ if ( pNxt )
+ {
+ pNxt->InvalidateLineNum_();
+ if ( pNxt != GetNext() )
+ pNxt->InvalidatePage();
+ }
+ }
+
+ SwTabFrame* pMasterTab(nullptr);
+ pFrame = GetIndNext();
+ if( pFrame )
+ {
+ // The old follow may have calculated a gap to the predecessor which
+ // now becomes obsolete or different as it becomes the first one itself
+ pFrame->InvalidatePrt_();
+ pFrame->InvalidatePos_();
+ pFrame->InvalidatePage( pPage );
+ if( pFrame->IsSctFrame() )
+ {
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pFrame )
+ {
+ pFrame->InvalidatePrt_();
+ pFrame->InvalidatePos_();
+ pFrame->InvalidatePage( pPage );
+ }
+ }
+ if( pFrame && IsInFootnote() )
+ pFrame->Prepare( PrepareHint::ErgoSum, nullptr, false );
+ if( IsInSct() && !GetPrev() )
+ {
+ SwSectionFrame* pSct = FindSctFrame();
+ if( !pSct->IsFollow() )
+ {
+ pSct->InvalidatePrt_();
+ pSct->InvalidatePage( pPage );
+ }
+ }
+ }
+ else
+ {
+ InvalidateNextPos();
+ //Someone needs to do the retouching: predecessor or upper
+ pFrame = GetPrev();
+ if ( nullptr != pFrame )
+ { pFrame->SetRetouche();
+ pFrame->Prepare( PrepareHint::WidowsOrphans );
+ pFrame->InvalidatePos_();
+ pFrame->InvalidatePage( pPage );
+ }
+ // If I'm (was) the only ContentFrame in my upper, it has to do the
+ // retouching. Also, perhaps a page became empty.
+ else
+ { SwRootFrame *pRoot = getRootFrame();
+ if ( pRoot )
+ {
+ pRoot->SetSuperfluous();
+ // RemoveSuperfluous can only remove empty pages at the end;
+ // find if there are pages without content following pPage
+ // and if so request a call to CheckPageDescs()
+ SwPageFrame const* pNext(pPage);
+ SwViewShell *pSh = pRoot->GetCurrShell();
+ if (pSh && pSh->Imp()->IsAction())
+ {
+ while ((pNext = static_cast<SwPageFrame const*>(pNext->GetNext())))
+ {
+ if (!sw::IsPageFrameEmpty(*pNext) && !pNext->IsFootnotePage())
+ {
+ pSh->Imp()->GetLayAction().SetCheckPageNum(pPage->GetPhyPageNum());
+ break;
+ }
+ }
+ }
+ GetUpper()->SetCompletePaint();
+ GetUpper()->InvalidatePage( pPage );
+ }
+ if( IsInSct() )
+ {
+ SwSectionFrame* pSct = FindSctFrame();
+ if( !pSct->IsFollow() )
+ {
+ pSct->InvalidatePrt_();
+ pSct->InvalidatePage( pPage );
+ }
+ }
+ // #i52253# The master table should take care
+ // of removing the follow flow line.
+ if ( IsInTab() )
+ {
+ SwTabFrame* pThisTab = FindTabFrame();
+ if (pThisTab && pThisTab->IsFollow())
+ {
+ pMasterTab = pThisTab->FindMaster();
+ }
+ }
+ }
+ }
+ //Remove first, then shrink the upper.
+ SwLayoutFrame *pUp = GetUpper();
+ RemoveFromLayout();
+ if ( !pUp )
+ {
+ assert(!pMasterTab);
+ return;
+ }
+
+ if (pMasterTab
+ && !pMasterTab->GetFollow()->GetFirstNonHeadlineRow()->ContainsContent())
+ { // only do this if there's no content in other cells of the row!
+ pMasterTab->InvalidatePos_();
+ pMasterTab->SetRemoveFollowFlowLinePending(true);
+ }
+
+ SwSectionFrame *pSct = nullptr;
+ if ( !pUp->Lower() &&
+ ( ( pUp->IsFootnoteFrame() && !pUp->IsColLocked() ) ||
+ ( pUp->IsInSct() &&
+ // #i29438#
+ // We have to consider the case that the section may be "empty"
+ // except from a temporary empty table frame.
+ // This can happen due to the new cell split feature.
+ !pUp->IsCellFrame() &&
+ // #126020# - adjust check for empty section
+ // #130797# - correct fix #126020#
+ !(pSct = pUp->FindSctFrame())->ContainsContent() &&
+ !pSct->ContainsAny( true ) ) ) )
+ {
+ if ( pUp->GetUpper() )
+ {
+
+ // prevent delete of <ColLocked> footnote frame
+ if ( pUp->IsFootnoteFrame() && !pUp->IsColLocked())
+ {
+ if( pUp->GetNext() && !pUp->GetPrev() )
+ {
+ SwFrame* pTmp = static_cast<SwLayoutFrame*>(pUp->GetNext())->ContainsAny();
+ if( pTmp )
+ pTmp->InvalidatePrt_();
+ }
+ if (!pUp->IsDeleteForbidden())
+ {
+ pUp->Cut();
+ SwFrame::DestroyFrame(pUp);
+ }
+ }
+ else
+ {
+
+ if ( pSct->IsColLocked() || !pSct->IsInFootnote() ||
+ ( pUp->IsFootnoteFrame() && pUp->IsColLocked() ) )
+ {
+ pSct->DelEmpty( false );
+ // If a locked section may not be deleted then at least
+ // its size became invalid after removing its last
+ // content.
+ pSct->InvalidateSize_();
+ }
+ else
+ {
+ pSct->DelEmpty( true );
+ SwFrame::DestroyFrame(pSct);
+ }
+ }
+ }
+ }
+ else
+ {
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nFrameHeight )
+ pUp->Shrink( nFrameHeight );
+ }
+}
+
+void SwLayoutFrame::Paste( SwFrame* pParent, SwFrame* pSibling)
+{
+ OSL_ENSURE( pParent, "No parent for pasting." );
+ OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
+ OSL_ENSURE( pParent != this, "I'm the parent oneself." );
+ OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
+ OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
+ "I'm still registered somewhere." );
+
+ //Insert in the tree.
+ InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
+
+ // OD 24.10.2002 #103517# - correct setting of variable <fnRect>
+ // <fnRect> is used for the following:
+ // (1) To invalidate the frame's size, if its size, which has to be the
+ // same as its upper/parent, differs from its upper's/parent's.
+ // (2) To adjust/grow the frame's upper/parent, if it has a dimension in its
+ // size, which is not determined by its upper/parent.
+ // Which size is which depends on the frame type and the layout direction
+ // (vertical or horizontal).
+ // There are the following cases:
+ // (A) Header and footer frames both in vertical and in horizontal layout
+ // have to size the width to the upper/parent. A dimension in the height
+ // has to cause an adjustment/grow of the upper/parent.
+ // --> <fnRect> = fnRectHori
+ // (B) Cell and column frames in vertical layout, the width has to be the
+ // same as upper/parent and a dimension in height causes adjustment/grow
+ // of the upper/parent.
+ // --> <fnRect> = fnRectHori
+ // in horizontal layout the other way around
+ // --> <fnRect> = fnRectVert
+ // (C) Other frames in vertical layout, the height has to be the
+ // same as upper/parent and a dimension in width causes adjustment/grow
+ // of the upper/parent.
+ // --> <fnRect> = fnRectVert
+ // in horizontal layout the other way around
+ // --> <fnRect> = fnRectHori
+ //SwRectFn fnRect = IsVertical() ? fnRectHori : fnRectVert;
+ SwRectFn fnRect;
+ if ( IsHeaderFrame() || IsFooterFrame() )
+ fnRect = fnRectHori;
+ else if ( IsCellFrame() || IsColumnFrame() )
+ fnRect = GetUpper()->IsVertical() ? fnRectHori : ( GetUpper()->IsVertLR() ? (GetUpper()->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert );
+ else
+ fnRect = GetUpper()->IsVertical() ? ( GetUpper()->IsVertLR() ? (GetUpper()->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+
+ if( (getFrameArea().*fnRect->fnGetWidth)() != (pParent->getFramePrintArea().*fnRect->fnGetWidth)())
+ InvalidateSize_();
+ InvalidatePos_();
+ const SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+ if( !IsColumnFrame() )
+ {
+ SwFrame *pFrame = GetIndNext();
+ if( nullptr != pFrame )
+ {
+ pFrame->InvalidatePos_();
+ if( IsInFootnote() )
+ {
+ if( pFrame->IsSctFrame() )
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pFrame )
+ pFrame->Prepare( PrepareHint::ErgoSum, nullptr, false );
+ }
+ }
+ if ( IsInFootnote() && nullptr != ( pFrame = GetIndPrev() ) )
+ {
+ if( pFrame->IsSctFrame() )
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pFrame )
+ pFrame->Prepare( PrepareHint::QuoVadis, nullptr, false );
+ }
+ }
+
+ if( !(getFrameArea().*fnRect->fnGetHeight)() )
+ return;
+
+ // AdjustNeighbourhood is now also called in columns which are not
+ // placed inside a frame
+ SwNeighbourAdjust nAdjust = GetUpper()->IsFootnoteBossFrame() ?
+ static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment()
+ : SwNeighbourAdjust::GrowShrink;
+ SwTwips nGrow = (getFrameArea().*fnRect->fnGetHeight)();
+ if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
+ AdjustNeighbourhood( nGrow );
+ else
+ {
+ SwTwips nReal = 0;
+ if( SwNeighbourAdjust::AdjustGrow == nAdjust )
+ nReal = AdjustNeighbourhood( nGrow );
+ if( nReal < nGrow )
+ nReal += pParent->Grow( nGrow - nReal );
+ if( SwNeighbourAdjust::GrowAdjust == nAdjust && nReal < nGrow )
+ AdjustNeighbourhood( nGrow - nReal );
+ }
+}
+
+void SwLayoutFrame::Cut()
+{
+ if ( GetNext() )
+ GetNext()->InvalidatePos_();
+
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nShrink = aRectFnSet.GetHeight(getFrameArea());
+
+ // Remove first, then shrink upper.
+ SwLayoutFrame *pUp = GetUpper();
+
+ // AdjustNeighbourhood is now also called in columns which are not
+ // placed inside a frame.
+
+ // Remove must not be called before an AdjustNeighbourhood, but it has to
+ // be called before the upper-shrink-call, if the upper-shrink takes care
+ // of its content.
+ if ( pUp && nShrink )
+ {
+ if( pUp->IsFootnoteBossFrame() )
+ {
+ SwNeighbourAdjust nAdjust= static_cast<SwFootnoteBossFrame*>(pUp)->NeighbourhoodAdjustment();
+ if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
+ AdjustNeighbourhood( -nShrink );
+ else
+ {
+ SwTwips nReal = 0;
+ if( SwNeighbourAdjust::AdjustGrow == nAdjust )
+ nReal = -AdjustNeighbourhood( -nShrink );
+ if( nReal < nShrink )
+ {
+ const SwTwips nOldHeight = aRectFnSet.GetHeight(getFrameArea());
+
+ // seems as if this needs to be forwarded to the SwFrame already here,
+ // changing to zero seems temporary anyways
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, 0 );
+ }
+
+ nReal += pUp->Shrink( nShrink - nReal );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nOldHeight );
+ }
+ }
+
+ if( SwNeighbourAdjust::GrowAdjust == nAdjust && nReal < nShrink )
+ AdjustNeighbourhood( nReal - nShrink );
+ }
+ RemoveFromLayout();
+ }
+ else
+ {
+ RemoveFromLayout();
+ pUp->Shrink( nShrink );
+ }
+ }
+ else
+ RemoveFromLayout();
+
+ if( pUp && !pUp->Lower() )
+ {
+ pUp->SetCompletePaint();
+ pUp->InvalidatePage();
+ }
+}
+
+SwTwips SwFrame::Grow( SwTwips nDist, bool bTst, bool bInfo )
+{
+ OSL_ENSURE( nDist >= 0, "Negative growth?" );
+
+ PROTOCOL_ENTER( this, bTst ? PROT::GrowTest : PROT::Grow, DbgAction::NONE, &nDist )
+
+ if ( nDist )
+ {
+ SwRectFnSet aRectFnSet(this);
+
+ SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ if( nPrtHeight > 0 && nDist > (LONG_MAX - nPrtHeight) )
+ nDist = LONG_MAX - nPrtHeight;
+
+ if ( IsFlyFrame() )
+ return static_cast<SwFlyFrame*>(this)->Grow_( nDist, bTst );
+ else if( IsSctFrame() )
+ return static_cast<SwSectionFrame*>(this)->Grow_( nDist, bTst );
+ else
+ {
+ if (IsCellFrame())
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
+ const SwTabFrame* pTab = FindTabFrame();
+
+ // NEW TABLES
+ if ( pTab->IsVertical() != IsVertical() ||
+ pThisCell->GetLayoutRowSpan() < 1 )
+ return 0;
+ }
+ const SwTwips nReal = GrowFrame( nDist, bTst, bInfo );
+ if( !bTst )
+ {
+ nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nPrtHeight + ( IsContentFrame() ? nDist : nReal ) );
+ }
+ return nReal;
+ }
+ }
+ return 0;
+}
+
+SwTwips SwFrame::Shrink( SwTwips nDist, bool bTst, bool bInfo )
+{
+ OSL_ENSURE( nDist >= 0, "Negative reduction?" );
+
+ PROTOCOL_ENTER( this, bTst ? PROT::ShrinkTest : PROT::Shrink, DbgAction::NONE, &nDist )
+
+ if ( nDist )
+ {
+ if ( IsFlyFrame() )
+ return static_cast<SwFlyFrame*>(this)->Shrink_( nDist, bTst );
+ else if( IsSctFrame() )
+ return static_cast<SwSectionFrame*>(this)->Shrink_( nDist, bTst );
+ else
+ {
+ if (IsCellFrame())
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
+ const SwTabFrame* pTab = FindTabFrame();
+
+ // NEW TABLES
+ if ( (pTab && pTab->IsVertical() != IsVertical()) ||
+ pThisCell->GetLayoutRowSpan() < 1 )
+ return 0;
+ }
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nReal = aRectFnSet.GetHeight(getFrameArea());
+ ShrinkFrame( nDist, bTst, bInfo );
+ nReal -= aRectFnSet.GetHeight(getFrameArea());
+ if( !bTst )
+ {
+ const SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nPrtHeight - ( IsContentFrame() ? nDist : nReal ) );
+ }
+ return nReal;
+ }
+ }
+ return 0;
+}
+
+/** Adjust surrounding neighbourhood after insertion
+ *
+ * A Frame needs "normalization" if it is directly placed below a footnote boss (page/column) and its
+ * size changes. There is always a frame that takes the maximum possible space (the frame that
+ * contains the Body text) and zero or more frames which only take the space needed (header/footer
+ * area, footnote container). If one of these frames changes, the body-text-frame has to grow or
+ * shrink accordingly, even though it's fixed.
+ *
+ * !! Is it possible to do this in a generic way and not restrict it to the page and a distinct
+ * frame which takes the maximum space (controlled using the FrameSize attribute)?
+ * Problems:
+ * - What if multiple frames taking the maximum space are placed next to each other?
+ * - How is the maximum space calculated?
+ * - How small can those frames become?
+ *
+ * In any case, only a certain amount of space is allowed, so we never go below a minimum value for
+ * the height of the body.
+ *
+ * @param nDiff the value around which the space has to be allocated
+ */
+SwTwips SwFrame::AdjustNeighbourhood( SwTwips nDiff, bool bTst )
+{
+ PROTOCOL_ENTER( this, PROT::AdjustN, DbgAction::NONE, &nDiff );
+
+ if ( !nDiff || !GetUpper()->IsFootnoteBossFrame() ) // only inside pages/columns
+ return 0;
+
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+
+ //The (Page-)Body only changes in BrowseMode, but only if it does not
+ //contain columns.
+ if ( IsPageBodyFrame() && (!bBrowse ||
+ (static_cast<SwLayoutFrame*>(this)->Lower() &&
+ static_cast<SwLayoutFrame*>(this)->Lower()->IsColumnFrame())) )
+ return 0;
+
+ //In BrowseView mode the PageFrame can handle some of the requests.
+ tools::Long nBrowseAdd = 0;
+ if ( bBrowse && GetUpper()->IsPageFrame() ) // only (Page-)BodyFrames
+ {
+ SwViewShell *pViewShell = getRootFrame()->GetCurrShell();
+ SwLayoutFrame *pUp = GetUpper();
+ tools::Long nChg;
+ const tools::Long nUpPrtBottom = pUp->getFrameArea().Height() -
+ pUp->getFramePrintArea().Height() - pUp->getFramePrintArea().Top();
+ SwRect aInva( pUp->getFrameArea() );
+ if ( pViewShell )
+ {
+ aInva.Pos().setX( pViewShell->VisArea().Left() );
+ aInva.Width( pViewShell->VisArea().Width() );
+ }
+ if ( nDiff > 0 )
+ {
+ nChg = BROWSE_HEIGHT - pUp->getFrameArea().Height();
+ nChg = std::min( nDiff, SwTwips(nChg) );
+
+ if ( !IsBodyFrame() )
+ {
+ SetCompletePaint();
+ if ( !pViewShell || pViewShell->VisArea().Height() >= pUp->getFrameArea().Height() )
+ {
+ //First minimize Body, it will grow again later.
+ SwFrame *pBody = static_cast<SwFootnoteBossFrame*>(pUp)->FindBodyCont();
+ const tools::Long nTmp = nChg - pBody->getFramePrintArea().Height();
+ if ( !bTst )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pBody);
+ aFrm.Height(std::max( tools::Long(0), aFrm.Height() - nChg ));
+ }
+
+ pBody->InvalidatePrt_();
+ pBody->InvalidateSize_();
+ if ( pBody->GetNext() )
+ pBody->GetNext()->InvalidatePos_();
+ if ( !IsHeaderFrame() )
+ pBody->SetCompletePaint();
+ }
+ nChg = nTmp <= 0 ? 0 : nTmp;
+ }
+ }
+
+ const tools::Long nTmp = nUpPrtBottom + 20;
+ aInva.Top( aInva.Bottom() - nTmp );
+ aInva.Height( nChg + nTmp );
+ }
+ else
+ {
+ //The page can shrink to 0. The first page keeps the same size like
+ //VisArea.
+ nChg = nDiff;
+ tools::Long nInvaAdd = 0;
+ if ( pViewShell && !pUp->GetPrev() &&
+ pUp->getFrameArea().Height() + nDiff < pViewShell->VisArea().Height() )
+ {
+ // This means that we have to invalidate adequately.
+ nChg = pViewShell->VisArea().Height() - pUp->getFrameArea().Height();
+ nInvaAdd = -(nDiff - nChg);
+ }
+
+ //Invalidate including bottom border.
+ tools::Long nBorder = nUpPrtBottom + 20;
+ nBorder -= nChg;
+ aInva.Top( aInva.Bottom() - (nBorder+nInvaAdd) );
+ if ( !IsBodyFrame() )
+ {
+ SetCompletePaint();
+ if ( !IsHeaderFrame() )
+ static_cast<SwFootnoteBossFrame*>(pUp)->FindBodyCont()->SetCompletePaint();
+ }
+ //Invalidate the page because of the frames. Thereby the page becomes
+ //the right size again if a frame didn't fit. This only works
+ //randomly for paragraph bound frames otherwise (NotifyFlys).
+ pUp->InvalidateSize();
+ }
+ if ( !bTst )
+ {
+ //Independent from nChg
+ if ( pViewShell && aInva.HasArea() && pUp->GetUpper() )
+ pViewShell->InvalidateWindows( aInva );
+ }
+ if ( !bTst && nChg )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pUp);
+ aFrm.AddHeight(nChg );
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pUp);
+ aPrt.AddHeight(nChg );
+ }
+
+ if ( pViewShell )
+ pViewShell->Imp()->SetFirstVisPageInvalid();
+
+ if ( GetNext() )
+ GetNext()->InvalidatePos_();
+
+ //Trigger a repaint if necessary.
+ std::unique_ptr<SvxBrushItem> aBack(pUp->GetFormat()->makeBackgroundBrushItem());
+ const SvxGraphicPosition ePos = aBack->GetGraphicPos();
+ if ( ePos != GPOS_NONE && ePos != GPOS_TILED )
+ pViewShell->InvalidateWindows( pUp->getFrameArea() );
+
+ if ( pUp->GetUpper() )
+ {
+ if ( pUp->GetNext() )
+ pUp->GetNext()->InvalidatePos();
+
+ //Sad but true: during notify on ViewImp a Calc on the page and
+ //its Lower may be called. The values should not be changed
+ //because the caller takes care of the adjustment of Frame and
+ //Prt.
+ const tools::Long nOldFrameHeight = getFrameArea().Height();
+ const tools::Long nOldPrtHeight = getFramePrintArea().Height();
+ const bool bOldComplete = IsCompletePaint();
+
+ if ( IsBodyFrame() )
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( nOldFrameHeight );
+ }
+
+ if ( pUp->GetUpper() )
+ {
+ static_cast<SwRootFrame*>(pUp->GetUpper())->CheckViewLayout( nullptr, nullptr );
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( nOldFrameHeight );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( nOldPrtHeight );
+
+ mbCompletePaint = bOldComplete;
+ }
+ if ( !IsBodyFrame() )
+ pUp->InvalidateSize_();
+ InvalidatePage( static_cast<SwPageFrame*>(pUp) );
+ }
+ nDiff -= nChg;
+ if ( !nDiff )
+ return nChg;
+ else
+ nBrowseAdd = nChg;
+ }
+
+ const SwFootnoteBossFrame *pBoss = static_cast<SwFootnoteBossFrame*>(GetUpper());
+
+ SwTwips nReal = 0,
+ nAdd = 0;
+ SwFrame *pFrame = nullptr;
+ SwRectFnSet aRectFnSet(this);
+
+ if( IsBodyFrame() )
+ {
+ if( IsInSct() )
+ {
+ SwSectionFrame *pSect = FindSctFrame();
+ if( nDiff > 0 && pSect->IsEndnAtEnd() && GetNext() &&
+ GetNext()->IsFootnoteContFrame() )
+ {
+ SwFootnoteContFrame* pCont = static_cast<SwFootnoteContFrame*>(GetNext());
+ SwTwips nMinH = 0;
+ SwFootnoteFrame* pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ bool bFootnote = false;
+ while( pFootnote )
+ {
+ if( !pFootnote->GetAttr()->GetFootnote().IsEndNote() )
+ {
+ nMinH += aRectFnSet.GetHeight(pFootnote->getFrameArea());
+ bFootnote = true;
+ }
+ pFootnote = static_cast<SwFootnoteFrame*>(pFootnote->GetNext());
+ }
+ if( bFootnote )
+ nMinH += aRectFnSet.GetTop(pCont->getFramePrintArea());
+ nReal = aRectFnSet.GetHeight(pCont->getFrameArea()) - nMinH;
+ if( nReal > nDiff )
+ nReal = nDiff;
+ if( nReal > 0 )
+ pFrame = GetNext();
+ else
+ nReal = 0;
+ }
+ if( !bTst && !pSect->IsColLocked() )
+ pSect->InvalidateSize();
+ }
+ if( !pFrame )
+ return nBrowseAdd;
+ }
+ else
+ {
+ const bool bFootnotePage = pBoss->IsPageFrame() && static_cast<const SwPageFrame*>(pBoss)->IsFootnotePage();
+ if ( bFootnotePage && !IsFootnoteContFrame() )
+ pFrame = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoss->FindFootnoteCont()));
+ if ( !pFrame )
+ pFrame = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoss->FindBodyCont()));
+
+ if ( !pFrame )
+ return 0;
+
+ //If not one is found, everything else is solved.
+ nReal = aRectFnSet.GetHeight(pFrame->getFrameArea());
+ if( nReal > nDiff )
+ nReal = nDiff;
+ if( !bFootnotePage )
+ {
+ //Respect the minimal boundary!
+ if( nReal )
+ {
+ const SwTwips nMax = pBoss->GetVarSpace();
+ if ( nReal > nMax )
+ nReal = nMax;
+ }
+ if( !IsFootnoteContFrame() && nDiff > nReal &&
+ pFrame->GetNext() && pFrame->GetNext()->IsFootnoteContFrame()
+ && ( pFrame->GetNext()->IsVertical() == IsVertical() )
+ )
+ {
+ //If the Body doesn't return enough, we look for a footnote, if
+ //there is one, we steal there accordingly.
+ const SwTwips nAddMax = aRectFnSet.GetHeight(pFrame->GetNext()->getFrameArea());
+ nAdd = nDiff - nReal;
+ if ( nAdd > nAddMax )
+ nAdd = nAddMax;
+ if ( !bTst )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame->GetNext());
+ aRectFnSet.SetHeight(aFrm, nAddMax-nAdd);
+
+ if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() )
+ {
+ aFrm.Pos().AdjustX(nAdd );
+ }
+ }
+
+ pFrame->GetNext()->InvalidatePrt();
+
+ if ( pFrame->GetNext()->GetNext() )
+ {
+ pFrame->GetNext()->GetNext()->InvalidatePos_();
+ }
+ }
+ }
+ }
+ }
+
+ if ( !bTst && nReal )
+ {
+ SwTwips nTmp = aRectFnSet.GetHeight(pFrame->getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame);
+ aRectFnSet.SetHeight( aFrm, nTmp - nReal );
+
+ if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() )
+ {
+ aFrm.Pos().AdjustX(nReal );
+ }
+ }
+
+ pFrame->InvalidatePrt();
+
+ if ( pFrame->GetNext() )
+ pFrame->GetNext()->InvalidatePos_();
+
+ if( nReal < 0 && pFrame->IsInSct() )
+ {
+ SwLayoutFrame* pUp = pFrame->GetUpper();
+ if( pUp && nullptr != ( pUp = pUp->GetUpper() ) && pUp->IsSctFrame() &&
+ !pUp->IsColLocked() )
+ pUp->InvalidateSize();
+ }
+ if( ( IsHeaderFrame() || IsFooterFrame() ) && pBoss->GetDrawObjs() )
+ {
+ const SwSortedObjs &rObjs = *pBoss->GetDrawObjs();
+ OSL_ENSURE( pBoss->IsPageFrame(), "Header/Footer out of page?" );
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ OSL_ENSURE( !pFly->IsFlyInContentFrame(), "FlyInCnt at Page?" );
+ const SwFormatVertOrient &rVert =
+ pFly->GetFormat()->GetVertOrient();
+ // When do we have to invalidate?
+ // If a frame is aligned on a PageTextArea and the header
+ // changes a TOP, MIDDLE or NONE aligned frame needs to
+ // recalculate it's position; if the footer changes a BOTTOM
+ // or MIDDLE aligned frame needs to recalculate it's
+ // position.
+ if( ( rVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ||
+ rVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA ) &&
+ ((IsHeaderFrame() && rVert.GetVertOrient()!=text::VertOrientation::BOTTOM) ||
+ (IsFooterFrame() && rVert.GetVertOrient()!=text::VertOrientation::NONE &&
+ rVert.GetVertOrient() != text::VertOrientation::TOP)) )
+ {
+ pFly->InvalidatePos_();
+ pFly->Invalidate_();
+ }
+ }
+ }
+ }
+ }
+ return (nBrowseAdd + nReal + nAdd);
+}
+
+/** method to perform additional actions on an invalidation (2004-05-19 #i28701#) */
+void SwFrame::ActionOnInvalidation( const InvalidationType )
+{
+ // default behaviour is to perform no additional action
+}
+
+/** method to determine, if an invalidation is allowed (2004-05-19 #i28701#) */
+bool SwFrame::InvalidationAllowed( const InvalidationType ) const
+{
+ // default behaviour is to allow invalidation
+ return true;
+}
+
+void SwFrame::ImplInvalidateSize()
+{
+ if ( InvalidationAllowed( INVALID_SIZE ) )
+ {
+ setFrameAreaSizeValid(false);
+
+ if ( IsFlyFrame() )
+ static_cast<SwFlyFrame*>(this)->Invalidate_();
+ else
+ InvalidatePage();
+
+ // OD 2004-05-19 #i28701#
+ ActionOnInvalidation( INVALID_SIZE );
+ }
+}
+
+void SwFrame::ImplInvalidatePrt()
+{
+ if ( InvalidationAllowed( INVALID_PRTAREA ) )
+ {
+ setFramePrintAreaValid(false);
+
+ if ( IsFlyFrame() )
+ static_cast<SwFlyFrame*>(this)->Invalidate_();
+ else
+ InvalidatePage();
+
+ // OD 2004-05-19 #i28701#
+ ActionOnInvalidation( INVALID_PRTAREA );
+ }
+}
+
+void SwFrame::ImplInvalidatePos()
+{
+ if ( !InvalidationAllowed( INVALID_POS ) )
+ return;
+
+ setFrameAreaPositionValid(false);
+
+ if ( IsFlyFrame() )
+ {
+ static_cast<SwFlyFrame*>(this)->Invalidate_();
+ }
+ else
+ {
+ InvalidatePage();
+ }
+
+ // OD 2004-05-19 #i28701#
+ ActionOnInvalidation( INVALID_POS );
+}
+
+void SwFrame::ImplInvalidateLineNum()
+{
+ if ( InvalidationAllowed( INVALID_LINENUM ) )
+ {
+ mbValidLineNum = false;
+ OSL_ENSURE( IsTextFrame(), "line numbers are implemented for text only" );
+ InvalidatePage();
+
+ // OD 2004-05-19 #i28701#
+ ActionOnInvalidation( INVALID_LINENUM );
+ }
+}
+
+void SwFrame::ReinitializeFrameSizeAttrFlags()
+{
+ const SwFormatFrameSize &rFormatSize = GetAttrSet()->GetFrameSize();
+ if ( SwFrameSize::Variable == rFormatSize.GetHeightSizeType() ||
+ SwFrameSize::Minimum == rFormatSize.GetHeightSizeType())
+ {
+ mbFixSize = false;
+ if ( GetType() & (SwFrameType::Header | SwFrameType::Footer | SwFrameType::Row) )
+ {
+ SwFrame *pFrame = static_cast<SwLayoutFrame*>(this)->Lower();
+ while ( pFrame )
+ { pFrame->InvalidateSize_();
+ pFrame->InvalidatePrt_();
+ pFrame = pFrame->GetNext();
+ }
+ SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(this)->ContainsContent();
+ // #i36991# - be save.
+ // E.g., a row can contain *no* content.
+ if ( pCnt )
+ {
+ pCnt->InvalidatePage();
+ do
+ {
+ pCnt->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ pCnt->InvalidateSize_();
+ pCnt = pCnt->GetNextContentFrame();
+ } while ( static_cast<SwLayoutFrame*>(this)->IsAnLower( pCnt ) );
+ }
+ }
+ }
+ else if ( rFormatSize.GetHeightSizeType() == SwFrameSize::Fixed )
+ {
+ if( IsVertical() )
+ ChgSize( Size( rFormatSize.GetWidth(), getFrameArea().Height()));
+ else
+ ChgSize( Size( getFrameArea().Width(), rFormatSize.GetHeight()));
+ }
+}
+
+void SwFrame::ValidateThisAndAllLowers( const sal_uInt16 nStage )
+{
+ // Stage 0: Only validate frames. Do not process any objects.
+ // Stage 1: Only validate fly frames and all of their contents.
+ // Stage 2: Validate all.
+
+ const bool bOnlyObject = 1 == nStage;
+ const bool bIncludeObjects = 1 <= nStage;
+
+ if ( !bOnlyObject || IsFlyFrame() )
+ {
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ setFrameAreaPositionValid(true);
+ }
+
+ if ( bIncludeObjects )
+ {
+ const SwSortedObjs* pObjs = GetDrawObjs();
+ if ( pObjs )
+ {
+ const size_t nCnt = pObjs->size();
+ for ( size_t i = 0; i < nCnt; ++i )
+ {
+ SwAnchoredObject* pAnchObj = (*pObjs)[i];
+ if ( auto pFlyFrame = pAnchObj->DynCastFlyFrame() )
+ pFlyFrame->ValidateThisAndAllLowers( 2 );
+ else if ( auto pAnchoredDrawObj = dynamic_cast<SwAnchoredDrawObject *>( pAnchObj ) )
+ pAnchoredDrawObj->ValidateThis();
+ }
+ }
+ }
+
+ if ( IsLayoutFrame() )
+ {
+ SwFrame* pLower = static_cast<SwLayoutFrame*>(this)->Lower();
+ while ( pLower )
+ {
+ pLower->ValidateThisAndAllLowers( nStage );
+ pLower = pLower->GetNext();
+ }
+ }
+}
+
+SwTwips SwContentFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwRectFnSet aRectFnSet(this);
+
+ SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nFrameHeight > 0 &&
+ nDist > (LONG_MAX - nFrameHeight ) )
+ nDist = LONG_MAX - nFrameHeight;
+
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+ SwFrameType nTmpType = SwFrameType::Cell | SwFrameType::Column;
+ if (bBrowse)
+ nTmpType |= SwFrameType::Body;
+ if( !(GetUpper()->GetType() & nTmpType) && GetUpper()->HasFixSize() )
+ {
+ if ( !bTst )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nFrameHeight + nDist );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX( -nDist );
+ }
+ }
+
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos();
+ }
+ // #i28701# - Due to the new object positioning the
+ // frame on the next page/column can flow backward (e.g. it was moved forward
+ // due to the positioning of its objects ). Thus, invalivate this next frame,
+ // if document compatibility option 'Consider wrapping style influence on
+ // object positioning' is ON.
+ else if ( GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ InvalidateNextPos();
+ }
+ }
+ return 0;
+ }
+
+ SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
+ SwFrame *pFrame = GetUpper()->Lower();
+ while( pFrame && nReal > 0 )
+ { nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ }
+
+ if ( !bTst )
+ {
+ //Contents are always resized to the wished value.
+ tools::Long nOld = aRectFnSet.GetHeight(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ aRectFnSet.SetHeight( aFrm, nOld + nDist );
+
+ if( IsVertical()&& !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX( -nDist );
+ }
+ }
+
+ SwTabFrame *pTab = (nOld && IsInTab()) ? FindTabFrame() : nullptr;
+ if (pTab)
+ {
+ if ( pTab->GetTable()->GetHTMLTableLayout() &&
+ !pTab->IsJoinLocked() &&
+ !pTab->GetFormat()->GetDoc()->GetDocShell()->IsReadOnly() )
+ {
+ pTab->InvalidatePos();
+ pTab->SetResizeHTMLTable();
+ }
+ }
+ }
+
+ //Only grow Upper if necessary.
+ if ( nReal < nDist )
+ {
+ if( GetUpper() )
+ {
+ if( bTst || !GetUpper()->IsFooterFrame() )
+ nReal = GetUpper()->Grow( nDist - std::max<tools::Long>(nReal, 0),
+ bTst, bInfo );
+ else
+ {
+ nReal = 0;
+ GetUpper()->InvalidateSize();
+ }
+ }
+ else
+ nReal = 0;
+ }
+ else
+ nReal = nDist;
+
+ // #i28701# - Due to the new object positioning the
+ // frame on the next page/column can flow backward (e.g. it was moved forward
+ // due to the positioning of its objects ). Thus, invalivate this next frame,
+ // if document compatibility option 'Consider wrapping style influence on
+ // object positioning' is ON.
+ if ( !bTst )
+ {
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos();
+ }
+ else if ( GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ InvalidateNextPos();
+ }
+ }
+
+ return nReal;
+}
+
+SwTwips SwContentFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwRectFnSet aRectFnSet(this);
+ OSL_ENSURE( nDist >= 0, "nDist < 0" );
+ OSL_ENSURE( nDist <= aRectFnSet.GetHeight(getFrameArea()),
+ "nDist > than current size." );
+
+ if ( !bTst )
+ {
+ SwTwips nRstHeight;
+ if( GetUpper() )
+ nRstHeight = aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()) );
+ else
+ nRstHeight = 0;
+ if( nRstHeight < 0 )
+ {
+ SwTwips nNextHeight = 0;
+ if( GetUpper()->IsSctFrame() && nDist > LONG_MAX/2 )
+ {
+ SwFrame *pNxt = GetNext();
+ while( pNxt )
+ {
+ nNextHeight += aRectFnSet.GetHeight(pNxt->getFrameArea());
+ pNxt = pNxt->GetNext();
+ }
+ }
+ nRstHeight = nDist + nRstHeight - nNextHeight;
+ }
+ else
+ {
+ nRstHeight = nDist;
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) - nDist );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nDist );
+ }
+ }
+
+ nDist = nRstHeight;
+ SwTabFrame *pTab = IsInTab() ? FindTabFrame() : nullptr;
+ if (pTab)
+ {
+ if ( pTab->GetTable()->GetHTMLTableLayout() &&
+ !pTab->IsJoinLocked() &&
+ !pTab->GetFormat()->GetDoc()->GetDocShell()->IsReadOnly() )
+ {
+ pTab->InvalidatePos();
+ pTab->SetResizeHTMLTable();
+ }
+ }
+ }
+
+ SwTwips nReal;
+ if( GetUpper() && nDist > 0 )
+ {
+ if( bTst || !GetUpper()->IsFooterFrame() )
+ nReal = GetUpper()->Shrink( nDist, bTst, bInfo );
+ else
+ {
+ nReal = 0;
+
+ // #108745# Sorry, dear old footer friend, I'm not gonna invalidate you,
+ // if there are any objects anchored inside your content, which
+ // overlap with the shrinking frame.
+ // This may lead to a footer frame that is too big, but this is better
+ // than looping.
+ // #109722# : The fix for #108745# was too strict.
+
+ bool bInvalidate = true;
+ const SwRect aRect( getFrameArea() );
+ const SwPageFrame* pPage = FindPageFrame();
+ const SwSortedObjs* pSorted = pPage ? pPage->GetSortedObjs() : nullptr;
+ if( pSorted )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pSorted)
+ {
+ const SwRect aBound( pAnchoredObj->GetObjRectWithSpaces() );
+
+ if( aBound.Left() > aRect.Right() )
+ continue;
+
+ if( aBound.Overlaps( aRect ) )
+ {
+ const SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
+ if( css::text::WrapTextMode_THROUGH != rFormat.GetSurround().GetSurround() )
+ {
+ const SwFrame* pAnchor = pAnchoredObj->GetAnchorFrame();
+ if ( pAnchor && pAnchor->FindFooterOrHeader() == GetUpper() )
+ {
+ bInvalidate = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if ( bInvalidate )
+ GetUpper()->InvalidateSize();
+ }
+ }
+ else
+ nReal = 0;
+
+ if ( !bTst )
+ {
+ //The position of the next Frame changes for sure.
+ InvalidateNextPos();
+
+ //If I don't have a successor I have to do the retouch by myself.
+ if ( !GetNext() )
+ SetRetouche();
+ }
+ return nReal;
+}
+
+void SwContentFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SwContentFrameInvFlags eInvFlags = SwContentFrameInvFlags::NONE;
+ if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which() && pLegacy->m_pOld)
+ {
+ auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ SfxItemIter aOIter(*rOldSetChg.GetChgSet());
+ SfxItemIter aNIter(*rNewSetChg.GetChgSet());
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ SwAttrSetChg aOldSet(rOldSetChg);
+ SwAttrSetChg aNewSet(rNewSetChg);
+ do
+ {
+ UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
+ pNItem = aNIter.NextItem();
+ pOItem = aOIter.NextItem();
+ } while(pNItem);
+ if(aOldSet.Count() || aNewSet.Count())
+ SwFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ else
+ UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if(eInvFlags == SwContentFrameInvFlags::NONE)
+ return;
+
+ SwPageFrame* pPage = FindPageFrame();
+ InvalidatePage(pPage);
+ if(eInvFlags & SwContentFrameInvFlags::SetCompletePaint)
+ SetCompletePaint();
+ if(eInvFlags & SwContentFrameInvFlags::InvalidatePos)
+ InvalidatePos_();
+ if(eInvFlags & SwContentFrameInvFlags::InvalidateSize)
+ InvalidateSize_();
+ if(eInvFlags & (SwContentFrameInvFlags::InvalidateSectPrt | SwContentFrameInvFlags::SetNextCompletePaint))
+ {
+ if(IsInSct() && !GetPrev())
+ {
+ SwSectionFrame* pSect = FindSctFrame();
+ if(pSect->ContainsAny() == this)
+ {
+ pSect->InvalidatePrt_();
+ pSect->InvalidatePage(pPage);
+ }
+ }
+ InvalidatePrt_();
+ }
+ SwFrame* pNextFrame = GetIndNext();
+ if(pNextFrame && eInvFlags & SwContentFrameInvFlags::InvalidateNextPrt)
+ {
+ pNextFrame->InvalidatePrt_();
+ pNextFrame->InvalidatePage(pPage);
+ }
+ if(pNextFrame && eInvFlags & SwContentFrameInvFlags::SetNextCompletePaint)
+ {
+ pNextFrame->SetCompletePaint();
+ }
+ if(eInvFlags & SwContentFrameInvFlags::InvalidatePrevPrt)
+ {
+ SwFrame* pPrevFrame = GetPrev();
+ if(pPrevFrame)
+ {
+ pPrevFrame->InvalidatePrt_();
+ pPrevFrame->InvalidatePage(pPage);
+ }
+ }
+ if(eInvFlags & SwContentFrameInvFlags::InvalidateNextPos)
+ InvalidateNextPos();
+}
+
+void SwContentFrame::UpdateAttr_( const SfxPoolItem* pOld, const SfxPoolItem* pNew,
+ SwContentFrameInvFlags &rInvFlags,
+ SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
+{
+ bool bClear = true;
+ sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ switch ( nWhich )
+ {
+ case RES_FMT_CHG:
+ rInvFlags = SwContentFrameInvFlags::SetCompletePaint
+ | SwContentFrameInvFlags::InvalidatePos
+ | SwContentFrameInvFlags::InvalidateSize
+ | SwContentFrameInvFlags::InvalidateSectPrt
+ | SwContentFrameInvFlags::InvalidateNextPrt
+ | SwContentFrameInvFlags::InvalidatePrevPrt
+ | SwContentFrameInvFlags::InvalidateNextPos
+ | SwContentFrameInvFlags::SetNextCompletePaint;
+ [[fallthrough]];
+
+ case RES_PAGEDESC: //attribute changes (on/off)
+ if ( IsInDocBody() && !IsInTab() )
+ {
+ rInvFlags |= SwContentFrameInvFlags::InvalidatePos;
+ SwPageFrame *pPage = FindPageFrame();
+ if ( !GetPrev() )
+ CheckPageDescs( pPage );
+ if (GetPageDescItem().GetNumOffset())
+ static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
+ SwDocPosUpdate aMsgHint( pPage->getFrameArea().Top() );
+ pPage->GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ }
+ break;
+
+ case RES_UL_SPACE:
+ {
+ // OD 2004-02-18 #106629# - correction
+ // Invalidation of the printing area of next frame, not only
+ // for footnote content.
+ if ( !GetIndNext() )
+ {
+ SwFrame* pNxt = FindNext();
+ if ( pNxt )
+ {
+ SwPageFrame* pPg = pNxt->FindPageFrame();
+ pNxt->InvalidatePage( pPg );
+ pNxt->InvalidatePrt_();
+ if( pNxt->IsSctFrame() )
+ {
+ SwFrame* pCnt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
+ if( pCnt )
+ {
+ pCnt->InvalidatePrt_();
+ pCnt->InvalidatePage( pPg );
+ }
+ }
+ pNxt->SetCompletePaint();
+ }
+ }
+ // OD 2004-03-17 #i11860#
+ if ( GetIndNext() &&
+ !GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_OBJECT_POS) )
+ {
+ // OD 2004-07-01 #i28701# - use new method <InvalidateObjs(..)>
+ GetIndNext()->InvalidateObjs();
+ }
+ Prepare( PrepareHint::ULSpaceChanged ); //TextFrame has to correct line spacing.
+ rInvFlags |= SwContentFrameInvFlags::SetNextCompletePaint;
+ [[fallthrough]];
+ }
+ case RES_LR_SPACE:
+ case RES_BOX:
+ case RES_SHADOW:
+ {
+ Prepare( PrepareHint::FixSizeChanged );
+ SwModify aMod;
+ SwFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ rInvFlags |= SwContentFrameInvFlags::InvalidateNextPrt | SwContentFrameInvFlags::InvalidatePrevPrt;
+ break;
+ }
+ case RES_BREAK:
+ {
+ rInvFlags |= SwContentFrameInvFlags::InvalidatePos | SwContentFrameInvFlags::InvalidateNextPos;
+ const IDocumentSettingAccess& rIDSA = GetUpper()->GetFormat()->getIDocumentSettingAccess();
+ if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) ||
+ rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) )
+ {
+ rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
+ SwFrame* pNxt = FindNext();
+ if( pNxt )
+ {
+ SwPageFrame* pPg = pNxt->FindPageFrame();
+ pNxt->InvalidatePage( pPg );
+ pNxt->InvalidatePrt_();
+ if( pNxt->IsSctFrame() )
+ {
+ SwFrame* pCnt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
+ if( pCnt )
+ {
+ pCnt->InvalidatePrt_();
+ pCnt->InvalidatePage( pPg );
+ }
+ }
+ pNxt->SetCompletePaint();
+ }
+ }
+ }
+ break;
+
+ // OD 2004-02-26 #i25029#
+ case RES_PARATR_CONNECT_BORDER:
+ {
+ rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
+ if ( IsTextFrame() )
+ {
+ InvalidateNextPrtArea();
+ }
+ if ( !GetIndNext() && IsInTab() && IsInSplitTableRow() )
+ {
+ FindTabFrame()->InvalidateSize();
+ }
+ }
+ break;
+
+ case RES_PARATR_TABSTOP:
+ case RES_CHRATR_SHADOWED:
+ case RES_CHRATR_AUTOKERN:
+ case RES_CHRATR_UNDERLINE:
+ case RES_CHRATR_OVERLINE:
+ case RES_CHRATR_KERNING:
+ case RES_CHRATR_FONT:
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_ESCAPEMENT:
+ case RES_CHRATR_CONTOUR:
+ case RES_PARATR_NUMRULE:
+ rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
+ break;
+
+ case RES_FRM_SIZE:
+ rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
+ [[fallthrough]];
+
+ default:
+ bClear = false;
+ }
+ if ( !bClear )
+ return;
+
+ if ( pOldSet || pNewSet )
+ {
+ if ( pOldSet )
+ pOldSet->ClearItem( nWhich );
+ if ( pNewSet )
+ pNewSet->ClearItem( nWhich );
+ }
+ else
+ {
+ SwModify aMod;
+ SwFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ }
+}
+
+SwLayoutFrame::SwLayoutFrame(SwFrameFormat *const pFormat, SwFrame *const pSib)
+ : SwFrame(pFormat, pSib)
+ , m_pLower(nullptr)
+{
+ const SwFormatFrameSize &rFormatSize = pFormat->GetFrameSize();
+ if ( rFormatSize.GetHeightSizeType() == SwFrameSize::Fixed )
+ mbFixSize = true;
+}
+
+// #i28701#
+
+SwTwips SwLayoutFrame::InnerHeight() const
+{
+ const SwFrame* pCnt = Lower();
+ if (!pCnt)
+ return 0;
+
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nRet = 0;
+ if( pCnt->IsColumnFrame() || pCnt->IsCellFrame() )
+ {
+ do
+ {
+ SwTwips nTmp = static_cast<const SwLayoutFrame*>(pCnt)->InnerHeight();
+ if( pCnt->isFramePrintAreaValid() )
+ nTmp += aRectFnSet.GetHeight(pCnt->getFrameArea()) -
+ aRectFnSet.GetHeight(pCnt->getFramePrintArea());
+ if( nRet < nTmp )
+ nRet = nTmp;
+ pCnt = pCnt->GetNext();
+ } while ( pCnt );
+ }
+ else
+ {
+ do
+ {
+ nRet += aRectFnSet.GetHeight(pCnt->getFrameArea());
+ if( pCnt->IsContentFrame() && static_cast<const SwTextFrame*>(pCnt)->IsUndersized() )
+ nRet += static_cast<const SwTextFrame*>(pCnt)->GetParHeight() -
+ aRectFnSet.GetHeight(pCnt->getFramePrintArea());
+ if( pCnt->IsLayoutFrame() && !pCnt->IsTabFrame() )
+ nRet += static_cast<const SwLayoutFrame*>(pCnt)->InnerHeight() -
+ aRectFnSet.GetHeight(pCnt->getFramePrintArea());
+ pCnt = pCnt->GetNext();
+ } while( pCnt );
+
+ }
+ return nRet;
+}
+
+SwTwips SwLayoutFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+ SwFrameType nTmpType = SwFrameType::Cell | SwFrameType::Column;
+ if (bBrowse)
+ nTmpType |= SwFrameType::Body;
+ if( !(GetType() & nTmpType) && HasFixSize() )
+ return 0;
+
+ SwRectFnSet aRectFnSet(this);
+ const SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ const SwTwips nFramePos = getFrameArea().Pos().X();
+
+ if ( nFrameHeight > 0 && nDist > (LONG_MAX - nFrameHeight) )
+ nDist = LONG_MAX - nFrameHeight;
+
+ SwTwips nMin = 0;
+ if ( GetUpper() && !IsCellFrame() )
+ {
+ SwFrame *pFrame = GetUpper()->Lower();
+ while( pFrame )
+ { nMin += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ }
+ nMin = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) - nMin;
+ if ( nMin < 0 )
+ nMin = 0;
+ }
+
+ SwRect aOldFrame( getFrameArea() );
+ bool bMoveAccFrame = false;
+
+ bool bChgPos = IsVertical();
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nFrameHeight + nDist );
+
+ if( bChgPos && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX( -nDist );
+ }
+
+ bMoveAccFrame = true;
+ }
+
+ SwTwips nReal = nDist - nMin;
+ if ( nReal > 0 )
+ {
+ if ( GetUpper() )
+ { // AdjustNeighbourhood now only for the columns (but not in frames)
+ SwNeighbourAdjust nAdjust = GetUpper()->IsFootnoteBossFrame() ?
+ static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment()
+ : SwNeighbourAdjust::GrowShrink;
+ if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
+ nReal = AdjustNeighbourhood( nReal, bTst );
+ else
+ {
+ if( SwNeighbourAdjust::AdjustGrow == nAdjust )
+ nReal += AdjustNeighbourhood( nReal, bTst );
+
+ SwTwips nGrow = 0;
+ if( 0 < nReal )
+ {
+ SwFrame* pToGrow = GetUpper();
+ // NEW TABLES
+ // A cell with a row span of > 1 is allowed to grow the
+ // line containing the end of the row span if it is
+ // located in the same table frame:
+ if (IsCellFrame())
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
+ if ( pThisCell->GetLayoutRowSpan() > 1 )
+ {
+ SwCellFrame& rEndCell = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( false ));
+ if ( -1 == rEndCell.GetTabBox()->getRowSpan() )
+ pToGrow = rEndCell.GetUpper();
+ else
+ pToGrow = nullptr;
+ }
+ }
+ nGrow = pToGrow ? pToGrow->Grow( nReal, bTst, bInfo ) : 0;
+ }
+
+ if( SwNeighbourAdjust::GrowAdjust == nAdjust && nGrow < nReal )
+ nReal = o3tl::saturating_add(nReal, AdjustNeighbourhood( nReal - nGrow, bTst ));
+
+ if ( IsFootnoteFrame() && (nGrow != nReal) && GetNext() )
+ {
+ //Footnotes can replace their successor.
+ SwTwips nSpace = bTst ? 0 : -nDist;
+ const SwFrame *pFrame = GetUpper()->Lower();
+ do
+ { nSpace += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ } while ( pFrame != GetNext() );
+ nSpace = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) -nSpace;
+ if ( nSpace < 0 )
+ nSpace = 0;
+ nSpace += nGrow;
+ if ( nReal > nSpace )
+ nReal = nSpace;
+ if ( nReal && !bTst )
+ static_cast<SwFootnoteFrame*>(this)->InvalidateNxtFootnoteCnts( FindPageFrame() );
+ }
+ else
+ nReal = nGrow;
+ }
+ }
+ else
+ nReal = 0;
+
+ nReal += nMin;
+ }
+ else
+ nReal = nDist;
+
+ if ( !bTst )
+ {
+ if( nReal != nDist &&
+ // NEW TABLES
+ ( !IsCellFrame() || static_cast<SwCellFrame*>(this)->GetLayoutRowSpan() > 1 ) )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nFrameHeight + nReal );
+
+ if( bChgPos && !IsVertLR() )
+ {
+ aFrm.Pos().setX( nFramePos - nReal );
+ }
+
+ bMoveAccFrame = true;
+ }
+
+ if ( nReal )
+ {
+ SwPageFrame *pPage = FindPageFrame();
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos_();
+ if (GetNext()->IsRowFrame())
+ { // also invalidate first cell
+ static_cast<SwLayoutFrame*>(GetNext())->Lower()->InvalidatePos_();
+ }
+ if ( GetNext()->IsContentFrame() )
+ GetNext()->InvalidatePage( pPage );
+ }
+ if ( !IsPageBodyFrame() )
+ {
+ InvalidateAll_();
+ InvalidatePage( pPage );
+ }
+ if (!(GetType() & (SwFrameType::Row|SwFrameType::Tab|SwFrameType::FtnCont|SwFrameType::Page|SwFrameType::Root)))
+ NotifyLowerObjs();
+
+ if( IsCellFrame() )
+ InvaPercentLowers( nReal );
+
+ std::unique_ptr<SvxBrushItem> aBack(GetFormat()->makeBackgroundBrushItem());
+ const SvxGraphicPosition ePos = aBack->GetGraphicPos();
+ if ( GPOS_NONE != ePos && GPOS_TILED != ePos )
+ SetCompletePaint();
+ }
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( bMoveAccFrame && IsAccessibleFrame() )
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
+ }
+ }
+#else
+ (void)bMoveAccFrame;
+ (void)aOldFrame;
+#endif
+
+ return nReal;
+}
+
+SwTwips SwLayoutFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+ SwFrameType nTmpType = SwFrameType::Cell | SwFrameType::Column;
+ if (bBrowse)
+ nTmpType |= SwFrameType::Body;
+
+ if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden())
+ {
+ if (IsBodyFrame())
+ {
+ // Whitespace is hidden and this body frame will not shrink, as it
+ // has a fix size.
+ // Invalidate the page frame size, so in case the reason for the
+ // shrink was that there is more whitespace on this page, the size
+ // without whitespace will be recalculated correctly.
+ SwPageFrame* pPageFrame = FindPageFrame();
+ pPageFrame->InvalidateSize();
+ }
+ }
+
+ if( !(GetType() & nTmpType) && HasFixSize() )
+ return 0;
+
+ OSL_ENSURE( nDist >= 0, "nDist < 0" );
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if ( nDist > nFrameHeight )
+ nDist = nFrameHeight;
+
+ SwTwips nMin = 0;
+ bool bChgPos = IsVertical();
+ if ( Lower() )
+ {
+ if( !Lower()->IsNeighbourFrame() )
+ { const SwFrame *pFrame = Lower();
+ const tools::Long nTmp = aRectFnSet.GetHeight(getFramePrintArea());
+ while( pFrame && nMin < nTmp )
+ { nMin += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ }
+ }
+ }
+ SwTwips nReal = nDist;
+ SwTwips nMinDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nMin;
+ if( nReal > nMinDiff )
+ nReal = nMinDiff;
+ if( nReal <= 0 )
+ return nDist;
+
+ SwRect aOldFrame( getFrameArea() );
+ bool bMoveAccFrame = false;
+
+ SwTwips nRealDist = nReal;
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nFrameHeight - nReal );
+
+ if( bChgPos && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nReal );
+ }
+
+ bMoveAccFrame = true;
+ }
+
+ SwNeighbourAdjust nAdjust = GetUpper() && GetUpper()->IsFootnoteBossFrame() ?
+ static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment()
+ : SwNeighbourAdjust::GrowShrink;
+
+ // AdjustNeighbourhood also in columns (but not in frames)
+ if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
+ {
+ if ( IsPageBodyFrame() && !bBrowse )
+ nReal = nDist;
+ else
+ { nReal = AdjustNeighbourhood( -nReal, bTst );
+ nReal *= -1;
+ if ( !bTst && IsBodyFrame() && nReal < nRealDist )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) + nRealDist - nReal );
+
+ if( bChgPos && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nRealDist - nReal );
+ }
+
+ OSL_ENSURE( !IsAccessibleFrame(), "bMoveAccFrame has to be set!" );
+ }
+ }
+ }
+ else if( IsColumnFrame() || IsColBodyFrame() )
+ {
+ SwTwips nTmp = GetUpper()->Shrink( nReal, bTst, bInfo );
+ if ( nTmp != nReal )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) + nReal - nTmp );
+
+ if( bChgPos && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nTmp - nReal );
+ }
+
+ OSL_ENSURE( !IsAccessibleFrame(), "bMoveAccFrame has to be set!" );
+ nReal = nTmp;
+ }
+ }
+ else
+ {
+ SwTwips nShrink = nReal;
+ SwFrame* pToShrink = GetUpper();
+ // NEW TABLES
+ if ( IsCellFrame() )
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
+ if ( pThisCell->GetLayoutRowSpan() > 1 )
+ {
+ SwCellFrame& rEndCell = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( false ));
+ pToShrink = rEndCell.GetUpper();
+ }
+ }
+
+ nReal = pToShrink ? pToShrink->Shrink( nShrink, bTst, bInfo ) : 0;
+ if( ( SwNeighbourAdjust::GrowAdjust == nAdjust || SwNeighbourAdjust::AdjustGrow == nAdjust )
+ && nReal < nShrink )
+ AdjustNeighbourhood( nReal - nShrink );
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( bMoveAccFrame && IsAccessibleFrame() )
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
+ }
+ }
+#else
+ (void)aOldFrame;
+ (void)bMoveAccFrame;
+#endif
+
+ if ( !bTst && (IsCellFrame() || IsColumnFrame() ? nReal : nRealDist) )
+ {
+ SwPageFrame *pPage = FindPageFrame();
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos_();
+ if ( GetNext()->IsContentFrame() )
+ GetNext()->InvalidatePage( pPage );
+ if ( IsTabFrame() )
+ static_cast<SwTabFrame*>(this)->SetComplete();
+ }
+ else
+ { if ( IsRetoucheFrame() )
+ SetRetouche();
+ if ( IsTabFrame() )
+ {
+ static_cast<SwTabFrame*>(this)->SetComplete();
+ if ( Lower() ) // Can also be in the Join and be empty!
+ InvalidateNextPos();
+ }
+ }
+ if ( !IsBodyFrame() )
+ {
+ InvalidateAll_();
+ InvalidatePage( pPage );
+ bool bCompletePaint = true;
+ const SwFrameFormat* pFormat = GetFormat();
+ if (pFormat)
+ {
+ std::unique_ptr<SvxBrushItem> aBack(pFormat->makeBackgroundBrushItem());
+ const SvxGraphicPosition ePos = aBack->GetGraphicPos();
+ if ( GPOS_NONE == ePos || GPOS_TILED == ePos )
+ bCompletePaint = false;
+ }
+ if (bCompletePaint)
+ SetCompletePaint();
+ }
+
+ if (!(GetType() & (SwFrameType::Row|SwFrameType::Tab|SwFrameType::FtnCont|SwFrameType::Page|SwFrameType::Root)))
+ NotifyLowerObjs();
+
+ if( IsCellFrame() )
+ InvaPercentLowers( nReal );
+
+ SwContentFrame *pCnt;
+ if( IsFootnoteFrame() && !static_cast<SwFootnoteFrame*>(this)->GetAttr()->GetFootnote().IsEndNote() &&
+ ( GetFormat()->GetDoc()->GetFootnoteInfo().m_ePos != FTNPOS_CHAPTER ||
+ ( IsInSct() && FindSctFrame()->IsFootnoteAtEnd() ) ) &&
+ nullptr != (pCnt = static_cast<SwFootnoteFrame*>(this)->GetRefFromAttr() ) )
+ {
+ if ( pCnt->IsFollow() )
+ { // If we are in another column/page than the frame with the
+ // reference, we don't need to invalidate its master.
+ SwFrame *pTmp = pCnt->FindFootnoteBossFrame(true) == FindFootnoteBossFrame(true)
+ ? &pCnt->FindMaster()->GetFrame() : pCnt;
+ pTmp->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ pTmp->InvalidateSize();
+ }
+ else
+ {
+ if (pCnt->FindPageFrame() == FindPageFrame())
+ {
+ pCnt->InvalidatePos();
+ }
+ else
+ {
+ SAL_WARN("sw.layout", "footnote frame on different page than ref frame?");
+ }
+ }
+ }
+ }
+ return nReal;
+}
+
+/**
+ * Changes the size of the directly subsidiary Frame's that have a fixed size, proportionally to the
+ * size change of the PrtArea of the Frame's.
+ *
+ * The variable Frames are also proportionally adapted; they will grow/shrink again by themselves.
+ */
+void SwLayoutFrame::ChgLowersProp( const Size& rOldSize )
+{
+ // no change of lower properties for root frame or if no lower exists.
+ if ( IsRootFrame() || !Lower() )
+ return;
+
+ // declare and init <SwFrame* pLowerFrame> with first lower
+ SwFrame *pLowerFrame = Lower();
+
+ // declare and init const booleans <bHeightChgd> and <bWidthChg>
+ const bool bHeightChgd = rOldSize.Height() != getFramePrintArea().Height();
+ const bool bWidthChgd = rOldSize.Width() != getFramePrintArea().Width();
+
+ SwRectFnSet aRectFnSet(this);
+
+ // This shortcut basically tries to handle only lower frames that
+ // are affected by the size change. Otherwise much more lower frames
+ // are invalidated.
+ if ( !( aRectFnSet.IsVert() ? bHeightChgd : bWidthChgd ) &&
+ ! Lower()->IsColumnFrame() &&
+ ( ( IsBodyFrame() && IsInDocBody() && ( !IsInSct() || !FindSctFrame()->IsColLocked() ) ) ||
+ // #i10826# Section frames without columns should not
+ // invalidate all lowers!
+ IsSctFrame() ) )
+ {
+ // Determine page frame the body frame resp. the section frame belongs to.
+ SwPageFrame *pPage = FindPageFrame();
+ // Determine last lower by traveling through them using <GetNext()>.
+ // During travel check each section frame, if it will be sized to
+ // maximum. If Yes, invalidate size of section frame and set
+ // corresponding flags at the page.
+ do
+ {
+ if( pLowerFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pLowerFrame)->ToMaximize_() )
+ {
+ pLowerFrame->InvalidateSize_();
+ pLowerFrame->InvalidatePage( pPage );
+ }
+ if( pLowerFrame->GetNext() )
+ pLowerFrame = pLowerFrame->GetNext();
+ else
+ break;
+ } while( true );
+ // If found last lower is a section frame containing no section
+ // (section frame isn't valid and will be deleted in the future),
+ // travel backwards.
+ while( pLowerFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pLowerFrame)->GetSection() &&
+ pLowerFrame->GetPrev() )
+ pLowerFrame = pLowerFrame->GetPrev();
+ // If found last lower is a section frame, set <pLowerFrame> to its last
+ // content, if the section frame is valid and is not sized to maximum.
+ // Otherwise set <pLowerFrame> to NULL - In this case body frame only
+ // contains invalid section frames.
+ if( pLowerFrame->IsSctFrame() )
+ pLowerFrame = static_cast<SwSectionFrame*>(pLowerFrame)->GetSection() &&
+ !static_cast<SwSectionFrame*>(pLowerFrame)->ToMaximize( false ) ?
+ static_cast<SwSectionFrame*>(pLowerFrame)->FindLastContent() : nullptr;
+
+ // continue with found last lower, probably the last content of a section
+ if ( pLowerFrame )
+ {
+ // If <pLowerFrame> is in a table frame, set <pLowerFrame> to this table
+ // frame and continue.
+ if ( pLowerFrame->IsInTab() )
+ {
+ // OD 28.10.2002 #97265# - safeguard for setting <pLowerFrame> to
+ // its table frame - check, if the table frame is also a lower
+ // of the body frame, in order to assure that <pLowerFrame> is not
+ // set to a frame, which is an *upper* of the body frame.
+ SwFrame* pTableFrame = pLowerFrame->FindTabFrame();
+ if ( IsAnLower( pTableFrame ) )
+ {
+ pLowerFrame = pTableFrame;
+ }
+ }
+ // Check, if variable size of body frame resp. section frame has grown
+ // OD 28.10.2002 #97265# - correct check, if variable size has grown.
+ SwTwips nOldHeight = aRectFnSet.IsVert() ? rOldSize.Width() : rOldSize.Height();
+ if( nOldHeight < aRectFnSet.GetHeight(getFramePrintArea()) )
+ {
+ // If variable size of body|section frame has grown, only found
+ // last lower and the position of the its next have to be invalidated.
+ pLowerFrame->InvalidateAll_();
+ pLowerFrame->InvalidatePage( pPage );
+ if( !pLowerFrame->IsFlowFrame() ||
+ !SwFlowFrame::CastFlowFrame( pLowerFrame )->HasFollow() )
+ pLowerFrame->InvalidateNextPos( true );
+ if ( pLowerFrame->IsTextFrame() )
+ static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ }
+ else
+ {
+ // variable size of body|section frame has shrunk. Thus,
+ // invalidate all lowers not matching the new body|section size
+ // and the dedicated new last lower.
+ if( aRectFnSet.IsVert() )
+ {
+ SwTwips nBot = getFrameArea().Left() + getFramePrintArea().Left();
+ while ( pLowerFrame && pLowerFrame->GetPrev() && pLowerFrame->getFrameArea().Left() < nBot )
+ {
+ pLowerFrame->InvalidateAll_();
+ pLowerFrame->InvalidatePage( pPage );
+ pLowerFrame = pLowerFrame->GetPrev();
+ }
+ }
+ else
+ {
+ SwTwips nBot = getFrameArea().Top() + getFramePrintArea().Bottom();
+ while ( pLowerFrame && pLowerFrame->GetPrev() && pLowerFrame->getFrameArea().Top() > nBot )
+ {
+ pLowerFrame->InvalidateAll_();
+ pLowerFrame->InvalidatePage( pPage );
+ pLowerFrame = pLowerFrame->GetPrev();
+ }
+ }
+ if ( pLowerFrame )
+ {
+ pLowerFrame->InvalidateSize_();
+ pLowerFrame->InvalidatePage( pPage );
+ if ( pLowerFrame->IsTextFrame() )
+ static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ }
+ }
+ // #i41694# - improvement by removing duplicates
+ if ( pLowerFrame )
+ {
+ if ( pLowerFrame->IsInSct() )
+ {
+ // #i41694# - follow-up of issue #i10826#
+ // No invalidation of section frame, if it's the this.
+ SwFrame* pSectFrame = pLowerFrame->FindSctFrame();
+ if( pSectFrame != this && IsAnLower( pSectFrame ) )
+ {
+ pSectFrame->InvalidateSize_();
+ pSectFrame->InvalidatePage( pPage );
+ }
+ }
+ }
+ }
+ return;
+ } // end of { special case }
+
+ // Invalidate page for content only once.
+ bool bInvaPageForContent = true;
+
+ // Declare booleans <bFixChgd> and <bVarChgd>, indicating for text frame
+ // adjustment, if fixed/variable size has changed.
+ bool bFixChgd, bVarChgd;
+ if( aRectFnSet.IsVert() == pLowerFrame->IsNeighbourFrame() )
+ {
+ bFixChgd = bWidthChgd;
+ bVarChgd = bHeightChgd;
+ }
+ else
+ {
+ bFixChgd = bHeightChgd;
+ bVarChgd = bWidthChgd;
+ }
+
+ // Declare const unsigned short <nFixWidth> and init it this frame types
+ // which has fixed width in vertical respectively horizontal layout.
+ // In vertical layout these are neighbour frames (cell and column frames),
+ // header frames and footer frames.
+ // In horizontal layout these are all frames, which aren't neighbour frames.
+ const SwFrameType nFixWidth = aRectFnSet.IsVert() ? (FRM_NEIGHBOUR | FRM_HEADFOOT)
+ : ~SwFrameType(FRM_NEIGHBOUR);
+
+ // Declare const unsigned short <nFixHeight> and init it this frame types
+ // which has fixed height in vertical respectively horizontal layout.
+ // In vertical layout these are all frames, which aren't neighbour frames,
+ // header frames, footer frames, body frames or foot note container frames.
+ // In horizontal layout these are neighbour frames.
+ const SwFrameType nFixHeight = aRectFnSet.IsVert() ? ~SwFrameType(FRM_NEIGHBOUR | FRM_HEADFOOT | FRM_BODYFTNC)
+ : FRM_NEIGHBOUR;
+
+ // Travel through all lowers using <GetNext()>
+ while ( pLowerFrame )
+ {
+ if ( pLowerFrame->IsTextFrame() )
+ {
+ // Text frames will only be invalidated - prepare invalidation
+ if ( bFixChgd )
+ static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::FixSizeChanged );
+ if ( bVarChgd )
+ static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ }
+ else
+ {
+ // If lower isn't a table, row, cell or section frame, adjust its
+ // frame size.
+ const SwFrameType nLowerType = pLowerFrame->GetType();
+ if ( !(nLowerType & (SwFrameType::Tab|SwFrameType::Row|SwFrameType::Cell|SwFrameType::Section)) )
+ {
+ if ( bWidthChgd )
+ {
+ if( nLowerType & nFixWidth )
+ {
+ // Considering previous conditions:
+ // In vertical layout set width of column, header and
+ // footer frames to its upper width.
+ // In horizontal layout set width of header, footer,
+ // foot note container, foot note, body and no-text
+ // frames to its upper width.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
+ aFrm.Width( getFramePrintArea().Width() );
+ }
+ else if( rOldSize.Width() && !pLowerFrame->IsFootnoteFrame() )
+ {
+ // Adjust frame width proportional, if lower isn't a
+ // foot note frame and condition <nLowerType & nFixWidth>
+ // isn't true.
+ // Considering previous conditions:
+ // In vertical layout these are foot note container,
+ // body and no-text frames.
+ // In horizontal layout these are column and no-text frames.
+ // OD 24.10.2002 #97265# - <double> calculation
+ // Perform <double> calculation of new width, if
+ // one of the coefficients is greater than 50000
+ SwTwips nNewWidth;
+ if ( (pLowerFrame->getFrameArea().Width() > 50000) ||
+ (getFramePrintArea().Width() > 50000) )
+ {
+ double nNewWidthTmp =
+ ( double(pLowerFrame->getFrameArea().Width())
+ * double(getFramePrintArea().Width()) )
+ / double(rOldSize.Width());
+ nNewWidth = SwTwips(nNewWidthTmp);
+ }
+ else
+ {
+ nNewWidth =
+ (pLowerFrame->getFrameArea().Width() * getFramePrintArea().Width()) / rOldSize.Width();
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
+ aFrm.Width( nNewWidth );
+ }
+ }
+ if ( bHeightChgd )
+ {
+ if( nLowerType & nFixHeight )
+ {
+ // Considering previous conditions:
+ // In vertical layout set height of foot note and
+ // no-text frames to its upper height.
+ // In horizontal layout set height of column frames
+ // to its upper height.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
+ aFrm.Height( getFramePrintArea().Height() );
+ }
+ // OD 01.10.2002 #102211#
+ // add conditions <!pLowerFrame->IsHeaderFrame()> and
+ // <!pLowerFrame->IsFooterFrame()> in order to avoid that
+ // the <Grow> of header or footer are overwritten.
+ // NOTE: Height of header/footer frame is determined by contents.
+ else if ( rOldSize.Height() &&
+ !pLowerFrame->IsFootnoteFrame() &&
+ !pLowerFrame->IsHeaderFrame() &&
+ !pLowerFrame->IsFooterFrame()
+ )
+ {
+ // Adjust frame height proportional, if lower isn't a
+ // foot note, a header or a footer frame and
+ // condition <nLowerType & nFixHeight> isn't true.
+ // Considering previous conditions:
+ // In vertical layout these are column, foot note container,
+ // body and no-text frames.
+ // In horizontal layout these are column, foot note
+ // container, body and no-text frames.
+
+ // OD 29.10.2002 #97265# - special case for page lowers
+ // The page lowers that have to be adjusted on page height
+ // change are the body frame and the foot note container
+ // frame.
+ // In vertical layout the height of both is directly
+ // adjusted to the page height change.
+ // In horizontal layout the height of the body frame is
+ // directly adjusted to the page height change and the
+ // foot note frame height isn't touched, because its
+ // determined by its content.
+ // OD 31.03.2003 #108446# - apply special case for page
+ // lowers - see description above - also for section columns.
+ if ( IsPageFrame() ||
+ ( IsColumnFrame() && IsInSct() )
+ )
+ {
+ OSL_ENSURE( pLowerFrame->IsBodyFrame() || pLowerFrame->IsFootnoteContFrame(),
+ "ChgLowersProp - only for body or foot note container" );
+ if ( pLowerFrame->IsBodyFrame() || pLowerFrame->IsFootnoteContFrame() )
+ {
+ if ( IsVertical() || pLowerFrame->IsBodyFrame() )
+ {
+ SwTwips nNewHeight =
+ pLowerFrame->getFrameArea().Height() +
+ ( getFramePrintArea().Height() - rOldSize.Height() );
+ if ( nNewHeight < 0)
+ {
+ // OD 01.04.2003 #108446# - adjust assertion condition and text
+ OSL_ENSURE( !( IsPageFrame() &&
+ (pLowerFrame->getFrameArea().Height()>0) &&
+ (pLowerFrame->isFrameAreaDefinitionValid()) ),
+ "ChgLowersProg - negative height for lower.");
+ nNewHeight = 0;
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
+ aFrm.Height( nNewHeight );
+ }
+ }
+ }
+ else
+ {
+ SwTwips nNewHeight;
+ // OD 24.10.2002 #97265# - <double> calculation
+ // Perform <double> calculation of new height, if
+ // one of the coefficients is greater than 50000
+ if ( (pLowerFrame->getFrameArea().Height() > 50000) ||
+ (getFramePrintArea().Height() > 50000) )
+ {
+ double nNewHeightTmp =
+ ( double(pLowerFrame->getFrameArea().Height())
+ * double(getFramePrintArea().Height()) )
+ / double(rOldSize.Height());
+ nNewHeight = SwTwips(nNewHeightTmp);
+ }
+ else
+ {
+ nNewHeight = ( pLowerFrame->getFrameArea().Height()
+ * getFramePrintArea().Height() ) / rOldSize.Height();
+ }
+ if( !pLowerFrame->GetNext() )
+ {
+ SwTwips nSum = getFramePrintArea().Height();
+ SwFrame* pTmp = Lower();
+ while( pTmp->GetNext() )
+ {
+ if( !pTmp->IsFootnoteContFrame() || !pTmp->IsVertical() )
+ nSum -= pTmp->getFrameArea().Height();
+ pTmp = pTmp->GetNext();
+ }
+ if( nSum - nNewHeight == 1 &&
+ nSum == pLowerFrame->getFrameArea().Height() )
+ nNewHeight = nSum;
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
+ aFrm.Height( nNewHeight );
+ }
+ }
+ }
+ }
+ } // end of else { NOT text frame }
+
+ pLowerFrame->InvalidateAll_();
+ if ( bInvaPageForContent && pLowerFrame->IsContentFrame() )
+ {
+ pLowerFrame->InvalidatePage();
+ bInvaPageForContent = false;
+ }
+
+ if ( !pLowerFrame->GetNext() && pLowerFrame->IsRetoucheFrame() )
+ {
+ //If a growth took place and the subordinate elements can retouch
+ //itself (currently Tabs, Sections and Content) we trigger it.
+ if ( rOldSize.Height() < getFramePrintArea().SSize().Height() ||
+ rOldSize.Width() < getFramePrintArea().SSize().Width() )
+ pLowerFrame->SetRetouche();
+ }
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+
+ // Finally adjust the columns if width is set to auto
+ // Possible optimization: execute this code earlier in this function and
+ // return???
+ if ( !(( (aRectFnSet.IsVert() && bHeightChgd) || (! aRectFnSet.IsVert() && bWidthChgd) ) &&
+ Lower()->IsColumnFrame()) )
+ return;
+
+ // get column attribute
+ const SwFormatCol* pColAttr = nullptr;
+ if ( IsPageBodyFrame() )
+ {
+ OSL_ENSURE( GetUpper()->IsPageFrame(), "Upper is not page frame" );
+ pColAttr = &GetUpper()->GetFormat()->GetCol();
+ }
+ else
+ {
+ OSL_ENSURE( IsFlyFrame() || IsSctFrame(), "Columns not in fly or section" );
+ pColAttr = &GetFormat()->GetCol();
+ }
+
+ if ( pColAttr->IsOrtho() && pColAttr->GetNumCols() > 1 )
+ AdjustColumns( pColAttr, false );
+}
+
+/** "Formats" the Frame; Frame and PrtArea.
+ *
+ * The Fixsize is not set here.
+ */
+void SwLayoutFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
+{
+ OSL_ENSURE( pAttrs, "LayoutFrame::Format, pAttrs is 0." );
+
+ if ( isFramePrintAreaValid() && isFrameAreaSizeValid() )
+ return;
+
+ bool bHideWhitespace = false;
+ if (IsPageFrame())
+ {
+ SwViewShell* pShell = getRootFrame()->GetCurrShell();
+ if (pShell && pShell->GetViewOptions()->IsWhitespaceHidden())
+ {
+ // This is needed so that no space is reserved for the margin on
+ // the last page of the document. Other pages would have no margin
+ // set even without this, as their frame height is the content
+ // height already.
+ bHideWhitespace = true;
+ }
+ }
+
+ const sal_uInt16 nLeft = o3tl::narrowing<sal_uInt16>(pAttrs->CalcLeft(this));
+ const sal_uInt16 nUpper = bHideWhitespace ? 0 : pAttrs->CalcTop();
+
+ const sal_uInt16 nRight = o3tl::narrowing<sal_uInt16>(pAttrs->CalcRight(this));
+ const sal_uInt16 nLower = bHideWhitespace ? 0 : pAttrs->CalcBottom();
+
+ const bool bVert = IsVertical() && !IsPageFrame();
+ SwRectFn fnRect = bVert ? ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+ (this->*fnRect->fnSetXMargins)( nLeft, nRight );
+ (this->*fnRect->fnSetYMargins)( nUpper, nLower );
+ }
+
+ if ( isFrameAreaSizeValid() )
+ return;
+
+ if ( !HasFixSize() )
+ {
+ const SwTwips nBorder = nUpper + nLower;
+ const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
+ SwTwips nMinHeight = rSz.GetHeightSizeType() == SwFrameSize::Minimum ? rSz.GetHeight() : 0;
+ do
+ {
+ setFrameAreaSizeValid(true);
+
+ //The size in VarSize is calculated using the content plus the
+ // borders.
+ SwTwips nRemaining = 0;
+ SwFrame *pFrame = Lower();
+ while ( pFrame )
+ { nRemaining += (pFrame->getFrameArea().*fnRect->fnGetHeight)();
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ // This TextFrame would like to be a bit bigger
+ nRemaining += static_cast<SwTextFrame*>(pFrame)->GetParHeight()
+ - (pFrame->getFramePrintArea().*fnRect->fnGetHeight)();
+ else if( pFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
+ nRemaining += static_cast<SwSectionFrame*>(pFrame)->Undersize();
+ pFrame = pFrame->GetNext();
+ }
+ nRemaining += nBorder;
+ nRemaining = std::max( nRemaining, nMinHeight );
+ const SwTwips nDiff = nRemaining-(getFrameArea().*fnRect->fnGetHeight)();
+ const tools::Long nOldLeft = (getFrameArea().*fnRect->fnGetLeft)();
+ const tools::Long nOldTop = (getFrameArea().*fnRect->fnGetTop)();
+ if ( nDiff )
+ {
+ if ( nDiff > 0 )
+ Grow( nDiff );
+ else
+ Shrink( -nDiff );
+ //Updates the positions using the fast channel.
+ MakePos();
+ }
+ //Don't exceed the bottom edge of the Upper.
+ if ( GetUpper() && (getFrameArea().*fnRect->fnGetHeight)() )
+ {
+ const SwTwips nLimit = (GetUpper()->*fnRect->fnGetPrtBottom)();
+ if( (this->*fnRect->fnSetLimit)( nLimit ) &&
+ nOldLeft == (getFrameArea().*fnRect->fnGetLeft)() &&
+ nOldTop == (getFrameArea().*fnRect->fnGetTop)() )
+ {
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ }
+ }
+ } while ( !isFrameAreaSizeValid() );
+ }
+ else if (GetType() & FRM_HEADFOOT)
+ {
+ do
+ { if ( getFrameArea().Height() != pAttrs->GetSize().Height() )
+ {
+ ChgSize( Size( getFrameArea().Width(), pAttrs->GetSize().Height()));
+ }
+
+ setFrameAreaSizeValid(true);
+ MakePos();
+ } while ( !isFrameAreaSizeValid() );
+ }
+ else
+ {
+ setFrameAreaSizeValid(true);
+ }
+
+ // While updating the size, PrtArea might be invalidated.
+ if (!isFramePrintAreaValid())
+ {
+ setFramePrintAreaValid(true);
+ (this->*fnRect->fnSetXMargins)(nLeft, nRight);
+ (this->*fnRect->fnSetYMargins)(nUpper, nLower);
+ }
+}
+
+static void InvaPercentFlys( SwFrame *pFrame, SwTwips nDiff )
+{
+ OSL_ENSURE( pFrame->GetDrawObjs(), "Can't find any Objects" );
+ for (SwAnchoredObject* pAnchoredObj : *pFrame->GetDrawObjs())
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ const SwFormatFrameSize &rSz = pFly->GetFormat()->GetFrameSize();
+ if ( rSz.GetWidthPercent() || rSz.GetHeightPercent() )
+ {
+ bool bNotify = true;
+ // If we've a fly with more than 90% relative height...
+ if( rSz.GetHeightPercent() > 90 && pFly->GetAnchorFrame() &&
+ rSz.GetHeightPercent() != SwFormatFrameSize::SYNCED && nDiff )
+ {
+ const SwFrame *pRel = pFly->IsFlyLayFrame() ? pFly->GetAnchorFrame():
+ pFly->GetAnchorFrame()->GetUpper();
+ // ... and we have already more than 90% height and we
+ // not allow the text to go through...
+ // then a notification could cause an endless loop, e.g.
+ // 100% height and no text wrap inside a cell of a table.
+ if( pFly->getFrameArea().Height()*10 >
+ ( nDiff + pRel->getFramePrintArea().Height() )*9 &&
+ pFly->GetFormat()->GetSurround().GetSurround() !=
+ css::text::WrapTextMode_THROUGH )
+ bNotify = false;
+ }
+ if( bNotify )
+ pFly->InvalidateSize();
+ }
+ }
+ }
+}
+
+void SwLayoutFrame::InvaPercentLowers( SwTwips nDiff )
+{
+ if ( GetDrawObjs() )
+ ::InvaPercentFlys( this, nDiff );
+
+ SwFrame *pFrame = ContainsContent();
+ if ( !pFrame )
+ return;
+
+ do
+ {
+ if ( pFrame->IsInTab() && !IsTabFrame() )
+ {
+ SwFrame *pTmp = pFrame->FindTabFrame();
+ OSL_ENSURE( pTmp, "Where's my TabFrame?" );
+ if( IsAnLower( pTmp ) )
+ pFrame = pTmp;
+ }
+
+ if ( pFrame->IsTabFrame() )
+ {
+ const SwFormatFrameSize &rSz = static_cast<SwLayoutFrame*>(pFrame)->GetFormat()->GetFrameSize();
+ if ( rSz.GetWidthPercent() || rSz.GetHeightPercent() )
+ pFrame->InvalidatePrt();
+ }
+ else if ( pFrame->GetDrawObjs() )
+ ::InvaPercentFlys( pFrame, nDiff );
+ pFrame = pFrame->FindNextCnt();
+ } while ( pFrame && IsAnLower( pFrame ) ) ;
+}
+
+tools::Long SwLayoutFrame::CalcRel( const SwFormatFrameSize &rSz ) const
+{
+ tools::Long nRet = rSz.GetWidth(),
+ nPercent = rSz.GetWidthPercent();
+
+ if ( nPercent )
+ {
+ const SwFrame *pRel = GetUpper();
+ tools::Long nRel = LONG_MAX;
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ if( pRel->IsPageBodyFrame() && pSh && bBrowseMode && pSh->VisArea().Width() )
+ {
+ nRel = pSh->GetBrowseWidth();
+ tools::Long nDiff = nRel - pRel->getFramePrintArea().Width();
+ if ( nDiff > 0 )
+ nRel -= nDiff;
+ }
+ nRel = std::min( nRel, pRel->getFramePrintArea().Width() );
+ nRet = nRel * nPercent / 100;
+ }
+ return nRet;
+}
+
+// Local helpers for SwLayoutFrame::FormatWidthCols()
+
+static tools::Long lcl_CalcMinColDiff( SwLayoutFrame *pLayFrame )
+{
+ tools::Long nDiff = 0, nFirstDiff = 0;
+ SwLayoutFrame *pCol = static_cast<SwLayoutFrame*>(pLayFrame->Lower());
+ OSL_ENSURE( pCol, "Where's the columnframe?" );
+ SwFrame *pFrame = pCol->Lower();
+ do
+ {
+ if( pFrame && pFrame->IsBodyFrame() )
+ pFrame = static_cast<SwBodyFrame*>(pFrame)->Lower();
+ if ( pFrame && pFrame->IsTextFrame() )
+ {
+ const tools::Long nTmp = static_cast<SwTextFrame*>(pFrame)->FirstLineHeight();
+ if ( nTmp != USHRT_MAX )
+ {
+ if ( pCol == pLayFrame->Lower() )
+ nFirstDiff = nTmp;
+ else
+ nDiff = nDiff ? std::min( nDiff, nTmp ) : nTmp;
+ }
+ }
+ //Skip empty columns!
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+ while ( pCol && nullptr == (pFrame = pCol->Lower()) )
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+
+ } while ( pFrame && pCol );
+
+ return nDiff ? nDiff : nFirstDiff ? nFirstDiff : 240;
+}
+
+static bool lcl_IsFlyHeightClipped( SwLayoutFrame *pLay )
+{
+ SwFrame *pFrame = pLay->ContainsContent();
+ while ( pFrame )
+ {
+ if ( pFrame->IsInTab() )
+ pFrame = pFrame->FindTabFrame();
+
+ if ( pFrame->GetDrawObjs() )
+ {
+ const size_t nCnt = pFrame->GetDrawObjs()->size();
+ for ( size_t i = 0; i < nCnt; ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsHeightClipped() &&
+ ( !pFly->IsFlyFreeFrame() || pFly->GetPageFrame() ) )
+ return true;
+ }
+ }
+ }
+ pFrame = pFrame->FindNextCnt();
+ }
+ return false;
+}
+
+void SwLayoutFrame::FormatWidthCols( const SwBorderAttrs &rAttrs,
+ const SwTwips nBorder, const SwTwips nMinHeight )
+{
+ //If there are columns involved, the size is adjusted using the last column.
+ //1. Format content.
+ //2. Calculate height of the last column: if it's too big, the Fly has to
+ // grow. The amount by which the Fly grows is not the amount of the
+ // overhang because we have to act on the assumption that some text flows
+ // back which will generate some more space.
+ // The amount which we grow by equals the overhang
+ // divided by the amount of columns or the overhang itself if it's smaller
+ // than the amount of columns.
+ //3. Go back to 1. until everything is stable.
+
+ const SwFormatCol &rCol = rAttrs.GetAttrSet().GetCol();
+ const sal_uInt16 nNumCols = rCol.GetNumCols();
+
+ bool bEnd = false;
+ bool bBackLock = false;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
+ vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
+ {
+ // Underlying algorithm
+ // We try to find the optimal height for the column.
+ // nMinimum starts with the passed minimum height and is then remembered
+ // as the maximum height on which column content still juts out of a
+ // column.
+ // nMaximum starts with LONG_MAX and is then remembered as the minimum
+ // width on which the content fitted.
+ // In column based sections nMaximum starts at the maximum value which
+ // the surrounding defines, this can certainly be a value on which
+ // content still juts out.
+ // The columns are formatted. If content still juts out, nMinimum is
+ // adjusted accordingly, then we grow, at least by uMinDiff but not
+ // over a certain nMaximum. If no content juts out but there is still
+ // some space left in the column, shrinking is done accordingly, at
+ // least by nMindIff but not below the nMinimum.
+ // Cancel as soon as no content juts out and the difference from minimum
+ // to maximum is less than MinDiff or the maximum which was defined by
+ // the surrounding is reached even if some content still juts out.
+
+ // Criticism of this implementation
+ // 1. Theoretically situations are possible in which the content fits in
+ // a lower height but not in a higher height. To ensure that the code
+ // handles such situations the code contains a few checks concerning
+ // minimum and maximum which probably are never triggered.
+ // 2. We use the same nMinDiff for shrinking and growing, but nMinDiff
+ // is more or less the smallest first line height and doesn't seem ideal
+ // as minimum value.
+
+ tools::Long nMinimum = nMinHeight;
+ tools::Long nMaximum;
+ bool bNoBalance = false;
+ SwRectFnSet aRectFnSet(this);
+ if( IsSctFrame() )
+ {
+ nMaximum = aRectFnSet.GetHeight(getFrameArea()) - nBorder +
+ aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
+ nMaximum += GetUpper()->Grow( LONG_MAX, true );
+ if( nMaximum < nMinimum )
+ {
+ if( nMaximum < 0 )
+ nMinimum = nMaximum = 0;
+ else
+ nMinimum = nMaximum;
+ }
+ if( nMaximum > BROWSE_HEIGHT )
+ nMaximum = BROWSE_HEIGHT;
+
+ bNoBalance = static_cast<SwSectionFrame*>(this)->GetSection()->GetFormat()->
+ GetBalancedColumns().GetValue();
+ SwFrame* pAny = ContainsAny();
+ if( bNoBalance ||
+ ( !aRectFnSet.GetHeight(getFrameArea()) && pAny ) )
+ {
+ tools::Long nTop = aRectFnSet.GetTopMargin(*this);
+ // #i23129# - correction
+ // to the calculated maximum height.
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nMaximum - aRectFnSet.GetHeight(getFrameArea()) );
+ }
+
+ if( nTop > nMaximum )
+ nTop = nMaximum;
+ aRectFnSet.SetYMargins( *this, nTop, 0 );
+ }
+ if( !pAny && !static_cast<SwSectionFrame*>(this)->IsFootnoteLock() )
+ {
+ SwFootnoteContFrame* pFootnoteCont = static_cast<SwSectionFrame*>(this)->ContainsFootnoteCont();
+ if( pFootnoteCont )
+ {
+ SwFrame* pFootnoteAny = pFootnoteCont->ContainsAny();
+ if( pFootnoteAny && pFootnoteAny->isFrameAreaDefinitionValid() )
+ {
+ bBackLock = true;
+ static_cast<SwSectionFrame*>(this)->SetFootnoteLock( true );
+ }
+ }
+ }
+ }
+ else
+ nMaximum = LONG_MAX;
+
+ // #i3317# - reset temporarily consideration
+ // of wrapping style influence
+ SwPageFrame* pPageFrame = FindPageFrame();
+ SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
+ {
+ pAnchoredObj->SetTmpConsiderWrapInfluence( false );
+ }
+ }
+ }
+ do
+ {
+ //Could take a while therefore check for Waitcrsr here.
+ if ( pImp )
+ pImp->CheckWaitCursor();
+
+ setFrameAreaSizeValid(true);
+ //First format the column as this will relieve the stack a bit.
+ //Also set width and height of the column (if they are wrong)
+ //while we are at it.
+ SwLayoutFrame *pCol = static_cast<SwLayoutFrame*>(Lower());
+
+ // #i27399#
+ // Simply setting the column width based on the values returned by
+ // CalcColWidth does not work for automatic column width.
+ AdjustColumns( &rCol, false );
+
+ for ( sal_uInt16 i = 0; i < nNumCols; ++i )
+ {
+ pCol->Calc(pRenderContext);
+ // ColumnFrames have a BodyFrame now, which needs to be calculated
+ pCol->Lower()->Calc(pRenderContext);
+ if( pCol->Lower()->GetNext() )
+ pCol->Lower()->GetNext()->Calc(pRenderContext); // SwFootnoteCont
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+ }
+
+ ::CalcContent( this );
+
+ pCol = static_cast<SwLayoutFrame*>(Lower());
+ OSL_ENSURE( pCol && pCol->GetNext(), ":-( column making holidays?");
+ // set bMinDiff if no empty columns exist
+ bool bMinDiff = true;
+ // OD 28.03.2003 #108446# - check for all column content and all columns
+ while ( bMinDiff && pCol )
+ {
+ bMinDiff = nullptr != pCol->ContainsContent();
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+ }
+ pCol = static_cast<SwLayoutFrame*>(Lower());
+ // OD 28.03.2003 #108446# - initialize local variable
+ SwTwips nDiff = 0;
+ SwTwips nMaxFree = 0;
+ SwTwips nAllFree = LONG_MAX;
+ // set bFoundLower if there is at least one non-empty column
+ bool bFoundLower = false;
+ while( pCol )
+ {
+ SwLayoutFrame* pLay = static_cast<SwLayoutFrame*>(pCol->Lower());
+ SwTwips nInnerHeight = aRectFnSet.GetHeight(pLay->getFrameArea()) -
+ aRectFnSet.GetHeight(pLay->getFramePrintArea());
+ if( pLay->Lower() )
+ {
+ bFoundLower = true;
+ nInnerHeight += pLay->InnerHeight();
+ }
+ else if( nInnerHeight < 0 )
+ nInnerHeight = 0;
+
+ if( pLay->GetNext() )
+ {
+ bFoundLower = true;
+ pLay = static_cast<SwLayoutFrame*>(pLay->GetNext());
+ OSL_ENSURE( pLay->IsFootnoteContFrame(),"FootnoteContainer expected" );
+ nInnerHeight += pLay->InnerHeight();
+ nInnerHeight += aRectFnSet.GetHeight(pLay->getFrameArea()) -
+ aRectFnSet.GetHeight(pLay->getFramePrintArea());
+ }
+ nInnerHeight -= aRectFnSet.GetHeight(pCol->getFramePrintArea());
+ if( nInnerHeight > nDiff )
+ {
+ nDiff = nInnerHeight;
+ nAllFree = 0;
+ }
+ else
+ {
+ if( nMaxFree < -nInnerHeight )
+ nMaxFree = -nInnerHeight;
+ if( nAllFree > -nInnerHeight )
+ nAllFree = -nInnerHeight;
+ }
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+ }
+
+ if ( bFoundLower || ( IsSctFrame() && static_cast<SwSectionFrame*>(this)->HasFollow() ) )
+ {
+ SwTwips nMinDiff = ::lcl_CalcMinColDiff( this );
+ // Here we decide if growing is needed - this is the case, if
+ // column content (nDiff) or a Fly juts over.
+ // In sections with columns we take into account to set the size
+ // when having a non-empty Follow.
+ if ( nDiff || ::lcl_IsFlyHeightClipped( this ) ||
+ ( IsSctFrame() && static_cast<SwSectionFrame*>(this)->CalcMinDiff( nMinDiff ) ) )
+ {
+ tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ // The minimum must not be smaller than our PrtHeight as
+ // long as something juts over.
+ if( nMinimum < nPrtHeight )
+ nMinimum = nPrtHeight;
+ // The maximum must not be smaller than PrtHeight if
+ // something still juts over.
+ if( nMaximum < nPrtHeight )
+ nMaximum = nPrtHeight; // Robust, but will this ever happen?
+ if( !nDiff ) // If only Flys jut over, we grow by nMinDiff
+ nDiff = nMinDiff;
+ // If we should grow more than by nMinDiff we split it over
+ // the columns
+ if ( std::abs(nDiff - nMinDiff) > nNumCols && nDiff > static_cast<tools::Long>(nNumCols) )
+ nDiff /= nNumCols;
+
+ if ( bMinDiff )
+ { // If no empty column exists, we want to grow at least
+ // by nMinDiff. Special case: If we are smaller than the
+ // minimal FrameHeight and PrtHeight is smaller than
+ // nMindiff we grow in a way that PrtHeight is exactly
+ // nMinDiff afterwards.
+ tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if ( nFrameHeight > nMinHeight || nPrtHeight >= nMinDiff )
+ nDiff = std::max( nDiff, nMinDiff );
+ else if( nDiff < nMinDiff )
+ nDiff = nMinDiff - nPrtHeight + 1;
+ }
+ // nMaximum has a size which fits the content or the
+ // requested value from the surrounding therefore we don't
+ // need to exceed this value.
+ if( nDiff + nPrtHeight > nMaximum )
+ nDiff = nMaximum - nPrtHeight;
+ }
+ else if( nMaximum > nMinimum ) // We fit, do we still have some margin?
+ {
+ tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ if ( nMaximum < nPrtHeight )
+ nDiff = nMaximum - nPrtHeight; // We grew over a working
+ // height and shrink back to it, but will this ever
+ // happen?
+ else
+ { // We have a new maximum, a size which fits for the content.
+ nMaximum = nPrtHeight;
+ // If the margin in the column is bigger than nMinDiff
+ // and we therefore drop under the minimum, we deflate
+ // a bit.
+ if ( !bNoBalance &&
+ // #i23129# - <nMinDiff> can be
+ // big, because of an object at the beginning of
+ // a column. Thus, decrease optimization here.
+ //nMaxFree >= nMinDiff &&
+ nMaxFree > 0 &&
+ ( !nAllFree ||
+ nMinimum < nPrtHeight - nMinDiff ) )
+ {
+ nMaxFree /= nNumCols; // disperse over the columns
+ nDiff = nMaxFree < nMinDiff ? -nMinDiff : -nMaxFree; // min nMinDiff
+ if( nPrtHeight + nDiff <= nMinimum ) // below the minimum?
+ nDiff = ( nMinimum - nMaximum ) / 2; // Take the center
+ }
+ else if( nAllFree )
+ {
+ nDiff = -nAllFree;
+ if( nPrtHeight + nDiff <= nMinimum ) // Less than minimum?
+ nDiff = ( nMinimum - nMaximum ) / 2; // Take the center
+ }
+ }
+ }
+ if( nDiff ) // now we shrink or grow...
+ {
+ Size aOldSz( getFramePrintArea().SSize() );
+ tools::Long nTop = aRectFnSet.GetTopMargin(*this);
+ nDiff = aRectFnSet.GetHeight(getFramePrintArea()) + nDiff + nBorder - aRectFnSet.GetHeight(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nDiff );
+ }
+
+ // #i68520#
+ SwFlyFrame *pFlyFrame = IsFlyFrame() ? static_cast<SwFlyFrame*>(this) : nullptr;
+ if (pFlyFrame)
+ {
+ pFlyFrame->InvalidateObjRectWithSpaces();
+ }
+ aRectFnSet.SetYMargins( *this, nTop, nBorder - nTop );
+ ChgLowersProp( aOldSz );
+ NotifyLowerObjs();
+
+ // #i3317# - reset temporarily consideration
+ // of wrapping style influence
+ SwPageFrame* pTmpPageFrame = FindPageFrame();
+ SwSortedObjs* pTmpObjs = pTmpPageFrame ? pTmpPageFrame->GetSortedObjs() : nullptr;
+ if ( pTmpObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pTmpObjs)
+ {
+ if ( IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
+ {
+ pAnchoredObj->SetTmpConsiderWrapInfluence( false );
+ }
+ }
+ }
+ //Invalidate suitable to nicely balance the Frames.
+ //- Every first one after the second column gets a
+ // InvalidatePos();
+ pCol = static_cast<SwLayoutFrame*>(Lower()->GetNext());
+ while ( pCol )
+ {
+ SwFrame *pLow = pCol->Lower();
+ if ( pLow )
+ pLow->InvalidatePos_();
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+ }
+ if( IsSctFrame() && static_cast<SwSectionFrame*>(this)->HasFollow() )
+ {
+ // If we created a Follow, we need to give its content
+ // the opportunity to flow back inside the CalcContent
+ SwContentFrame* pTmpContent =
+ static_cast<SwSectionFrame*>(this)->GetFollow()->ContainsContent();
+ if( pTmpContent )
+ pTmpContent->InvalidatePos_();
+ }
+ }
+ else
+ bEnd = true;
+ }
+ else
+ bEnd = true;
+
+ } while ( !bEnd || !isFrameAreaSizeValid() );
+ }
+ // OD 01.04.2003 #108446# - Don't collect endnotes for sections. Thus, set
+ // 2nd parameter to <true>.
+ ::CalcContent( this, true );
+ if( IsSctFrame() )
+ {
+ // OD 14.03.2003 #i11760# - adjust 2nd parameter - sal_True --> true
+ setFrameAreaSizeValid(true);
+ ::CalcContent( this, true );
+ if( bBackLock )
+ static_cast<SwSectionFrame*>(this)->SetFootnoteLock( false );
+ }
+}
+
+static SwContentFrame* lcl_InvalidateSection( SwFrame *pCnt, SwInvalidateFlags nInv )
+{
+ SwSectionFrame* pSect = pCnt->FindSctFrame();
+ // If our ContentFrame is placed inside a table or a footnote, only sections
+ // which are also placed inside are meant.
+ // Exception: If a table is directly passed.
+ if( ( ( pCnt->IsInTab() && !pSect->IsInTab() ) ||
+ ( pCnt->IsInFootnote() && !pSect->IsInFootnote() ) ) && !pCnt->IsTabFrame() )
+ return nullptr;
+ if( nInv & SwInvalidateFlags::Size )
+ pSect->InvalidateSize_();
+ if( nInv & SwInvalidateFlags::Pos )
+ pSect->InvalidatePos_();
+ if( nInv & SwInvalidateFlags::PrtArea )
+ pSect->InvalidatePrt_();
+ SwFlowFrame *pFoll = pSect->GetFollow();
+ // Temporary separation from follow
+ pSect->SetFollow( nullptr );
+ SwContentFrame* pRet = pSect->FindLastContent();
+ pSect->SetFollow( pFoll );
+ return pRet;
+}
+
+static SwContentFrame* lcl_InvalidateTable( SwTabFrame *pTable, SwInvalidateFlags nInv )
+{
+ if( ( nInv & SwInvalidateFlags::Section ) && pTable->IsInSct() )
+ lcl_InvalidateSection( pTable, nInv );
+ if( nInv & SwInvalidateFlags::Size )
+ pTable->InvalidateSize_();
+ if( nInv & SwInvalidateFlags::Pos )
+ pTable->InvalidatePos_();
+ if( nInv & SwInvalidateFlags::PrtArea )
+ pTable->InvalidatePrt_();
+ return pTable->FindLastContent();
+}
+
+static void lcl_InvalidateAllContent( SwContentFrame *pCnt, SwInvalidateFlags nInv );
+
+static void lcl_InvalidateContent( SwContentFrame *pCnt, SwInvalidateFlags nInv )
+{
+ SwContentFrame *pLastTabCnt = nullptr;
+ SwContentFrame *pLastSctCnt = nullptr;
+ while ( pCnt )
+ {
+ if( nInv & SwInvalidateFlags::Section )
+ {
+ if( pCnt->IsInSct() )
+ {
+ // See above at tables
+ if( !pLastSctCnt )
+ pLastSctCnt = lcl_InvalidateSection( pCnt, nInv );
+ if( pLastSctCnt == pCnt )
+ pLastSctCnt = nullptr;
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ OSL_ENSURE( !pLastSctCnt, "Where's the last SctContent?" );
+#endif
+ }
+ if( nInv & SwInvalidateFlags::Table )
+ {
+ if( pCnt->IsInTab() )
+ {
+ // To not call FindTabFrame() for each ContentFrame of a table and
+ // then invalidate the table, we remember the last ContentFrame of
+ // the table and ignore IsInTab() until we are past it.
+ // When entering the table, LastSctCnt is set to null, so
+ // sections inside the table are correctly invalidated.
+ // If the table itself is in a section the
+ // invalidation is done three times, which is acceptable.
+ if( !pLastTabCnt )
+ {
+ pLastTabCnt = lcl_InvalidateTable( pCnt->FindTabFrame(), nInv );
+ pLastSctCnt = nullptr;
+ }
+ if( pLastTabCnt == pCnt )
+ {
+ pLastTabCnt = nullptr;
+ pLastSctCnt = nullptr;
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ OSL_ENSURE( !pLastTabCnt, "Where's the last TabContent?" );
+#endif
+ }
+
+ if( nInv & SwInvalidateFlags::Size )
+ pCnt->Prepare( PrepareHint::Clear, nullptr, false );
+ if( nInv & SwInvalidateFlags::Pos )
+ pCnt->InvalidatePos_();
+ if( nInv & SwInvalidateFlags::PrtArea )
+ pCnt->InvalidatePrt_();
+ if ( nInv & SwInvalidateFlags::LineNum )
+ pCnt->InvalidateLineNum();
+ if ( pCnt->GetDrawObjs() )
+ lcl_InvalidateAllContent( pCnt, nInv );
+ pCnt = pCnt->GetNextContentFrame();
+ }
+}
+
+static void lcl_InvalidateAllContent( SwContentFrame *pCnt, SwInvalidateFlags nInv )
+{
+ SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ ::lcl_InvalidateContent( pFly->ContainsContent(), nInv );
+ if( nInv & SwInvalidateFlags::Direction )
+ pFly->CheckDirChange();
+ }
+ }
+ }
+}
+
+void SwRootFrame::InvalidateAllContent( SwInvalidateFlags nInv )
+{
+ // First process all page bound FlyFrames.
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
+ while( pPage )
+ {
+ pPage->InvalidateFlyLayout();
+ pPage->InvalidateFlyContent();
+ pPage->InvalidateFlyInCnt();
+ pPage->InvalidateLayout();
+ pPage->InvalidateContent();
+ pPage->InvalidatePage( pPage ); // So even the Turbo disappears if applicable
+
+ if ( pPage->GetSortedObjs() )
+ {
+ const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ ::lcl_InvalidateContent( pFly->ContainsContent(), nInv );
+ if ( nInv & SwInvalidateFlags::Direction )
+ pFly->CheckDirChange();
+ }
+ }
+ }
+ if( nInv & SwInvalidateFlags::Direction )
+ pPage->CheckDirChange();
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+
+ //Invalidate the whole document content and the character bound Flys here.
+ ::lcl_InvalidateContent( ContainsContent(), nInv );
+
+ if( nInv & SwInvalidateFlags::PrtArea )
+ {
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh )
+ pSh->InvalidateWindows( getFrameArea() );
+ }
+}
+
+/**
+ * Invalidate/re-calculate the position of all floating screen objects (Writer fly frames and
+ * drawing objects), that are anchored to paragraph or to character. (2004-03-16 #i11860#)
+ */
+void SwRootFrame::InvalidateAllObjPos()
+{
+ const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>(Lower());
+ while( pPageFrame )
+ {
+ pPageFrame->InvalidateFlyLayout();
+
+ if ( pPageFrame->GetSortedObjs() )
+ {
+ const SwSortedObjs& rObjs = *(pPageFrame->GetSortedObjs());
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ const SwFormatAnchor& rAnch = pAnchoredObj->GetFrameFormat().GetAnchor();
+ if ((rAnch.GetAnchorId() != RndStdIds::FLY_AT_PARA) &&
+ (rAnch.GetAnchorId() != RndStdIds::FLY_AT_CHAR))
+ {
+ // only to paragraph and to character anchored objects are considered.
+ continue;
+ }
+ // #i28701# - special invalidation for anchored
+ // objects, whose wrapping style influence has to be considered.
+ if ( pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() )
+ pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
+ else
+ pAnchoredObj->InvalidateObjPos();
+ }
+ }
+
+ pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
+ }
+}
+
+static void AddRemoveFlysForNode(
+ SwTextFrame & rFrame, SwTextNode & rTextNode,
+ std::set<SwNodeOffset> *const pSkipped,
+ const SwFrameFormats & rTable,
+ SwPageFrame *const pPage,
+ SwTextNode const*const pNode,
+ std::vector<sw::Extent>::const_iterator const& rIterFirst,
+ std::vector<sw::Extent>::const_iterator const& rIterEnd,
+ SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
+{
+ if (pNode == &rTextNode)
+ { // remove existing hidden at-char anchored flys
+ RemoveHiddenObjsOfNode(rTextNode, &rIterFirst, &rIterEnd, pFirstNode, pLastNode);
+ }
+ else if (rTextNode.GetIndex() < pNode->GetIndex())
+ {
+ // pNode's frame has been deleted by CheckParaRedlineMerge()
+ AppendObjsOfNode(&rTable,
+ pNode->GetIndex(), &rFrame, pPage, &rTextNode.GetDoc(),
+ &rIterFirst, &rIterEnd, pFirstNode, pLastNode);
+ if (pSkipped)
+ {
+ // if a fly has been added by AppendObjsOfNode, it must be skipped; if not, then it doesn't matter if it's skipped or not because it has no frames and because of that it would be skipped anyway
+ for (auto const pFly : pNode->GetAnchoredFlys())
+ {
+ if (pFly->Which() != RES_DRAWFRMFMT)
+ {
+ pSkipped->insert(pFly->GetContent().GetContentIdx()->GetIndex());
+ }
+ }
+ }
+ }
+}
+
+namespace sw {
+
+/// rTextNode is the first one of the "new" merge - if rTextNode isn't the same
+/// as MergedPara::pFirstNode, then nodes before rTextNode have their flys
+/// already properly attached, so only the other nodes need handling here.
+void AddRemoveFlysAnchoredToFrameStartingAtNode(
+ SwTextFrame & rFrame, SwTextNode & rTextNode,
+ std::set<SwNodeOffset> *const pSkipped)
+{
+ auto const pMerged(rFrame.GetMergedPara());
+ if (!pMerged
+ // do this only *once*, for the *last* frame
+ // otherwise AppendObj would create multiple frames for fly-frames!
+ || rFrame.GetFollow())
+ return;
+
+ assert(pMerged->pFirstNode->GetIndex() <= rTextNode.GetIndex()
+ && rTextNode.GetIndex() <= pMerged->pLastNode->GetIndex());
+ // add visible flys in non-first node to merged frame
+ // (hidden flys remain and are deleted via DelFrames())
+ SwFrameFormats& rTable(*rTextNode.GetDoc().GetSpzFrameFormats());
+ SwPageFrame *const pPage(rFrame.FindPageFrame());
+ std::vector<sw::Extent>::const_iterator iterFirst(pMerged->extents.begin());
+ std::vector<sw::Extent>::const_iterator iter(iterFirst);
+ SwTextNode const* pNode(pMerged->pFirstNode);
+ for ( ; ; ++iter)
+ {
+ if (iter == pMerged->extents.end()
+ || iter->pNode != pNode)
+ {
+ AddRemoveFlysForNode(rFrame, rTextNode, pSkipped, rTable, pPage,
+ pNode, iterFirst, iter,
+ pMerged->pFirstNode, pMerged->pLastNode);
+ SwNodeOffset const until = iter == pMerged->extents.end()
+ ? pMerged->pLastNode->GetIndex() + 1
+ : iter->pNode->GetIndex();
+ for (SwNodeOffset 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)
+ {
+ AddRemoveFlysForNode(rFrame, rTextNode, pSkipped,
+ rTable, pPage, pTmp->GetTextNode(), iter, iter,
+ pMerged->pFirstNode, pMerged->pLastNode);
+ }
+ }
+ if (iter == pMerged->extents.end())
+ {
+ break;
+ }
+ pNode = iter->pNode;
+ iterFirst = iter;
+ }
+ }
+}
+
+} // namespace sw
+
+static void UnHideRedlines(SwRootFrame & rLayout,
+ SwNodes & rNodes, SwNode const& rEndOfSectionNode,
+ std::set<SwNodeOffset> *const pSkipped)
+{
+ assert(rEndOfSectionNode.IsEndNode());
+ assert(rNodes[rEndOfSectionNode.StartOfSectionNode()->GetIndex() + 1]->IsCreateFrameWhenHidingRedlines()); // first node is never hidden
+ for (SwNodeOffset i = rEndOfSectionNode.StartOfSectionNode()->GetIndex() + 1;
+ i < rEndOfSectionNode.GetIndex(); ++i)
+ {
+ SwNode & rNode(*rNodes[i]);
+ if (rNode.IsTextNode()) // only text nodes are 1st node of a merge
+ {
+ SwTextNode & rTextNode(*rNode.GetTextNode());
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rTextNode);
+ std::vector<SwTextFrame*> frames;
+ for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame() == &rLayout)
+ {
+ if (pFrame->IsFollow())
+ {
+ frames.push_back(pFrame);
+ } // when hiding, the loop must remove the anchored flys
+ else // *before* resetting SetMergedPara anywhere - else
+ { // the fly deletion code will access multiple of the
+ // frames with inconsistent MergedPara and assert
+ frames.insert(frames.begin(), pFrame);
+ }
+ }
+ }
+ // this messes with pRegisteredIn so do it outside SwIterator
+ auto eMode(sw::FrameMode::Existing);
+ for (SwTextFrame * pFrame : frames)
+ {
+ if (rLayout.HasMergedParas())
+ {
+ assert(!pFrame->GetMergedPara() ||
+ !rNode.IsCreateFrameWhenHidingRedlines() ||
+ // FIXME: skip this assert in tables with deleted rows
+ pFrame->IsInTab());
+ if (rNode.IsCreateFrameWhenHidingRedlines())
+ {
+ {
+ auto pMerged(CheckParaRedlineMerge(*pFrame,
+ rTextNode, eMode));
+ pFrame->SetMergedPara(std::move(pMerged));
+ }
+ auto const pMerged(pFrame->GetMergedPara());
+ if (pMerged)
+ {
+ // invalidate SwInvalidateFlags::Size
+ pFrame->Prepare(PrepareHint::Clear, nullptr, false);
+ pFrame->InvalidatePage();
+ if (auto const pObjs = pFrame->GetDrawObjs())
+ { // also invalidate position of existing flys
+ // because they may need to be moved
+ for (auto const pObject : *pObjs)
+ {
+ pObject->InvalidateObjPos();
+ }
+ }
+ }
+ sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, rTextNode, pSkipped);
+ // only *first* frame of node gets Existing because it
+ eMode = sw::FrameMode::New; // is not idempotent!
+ }
+ }
+ else
+ {
+ if (auto const& pMergedPara = pFrame->GetMergedPara())
+ {
+ // invalidate SwInvalidateFlags::Size
+ pFrame->Prepare(PrepareHint::Clear, nullptr, false);
+ pFrame->InvalidatePage();
+ if (auto const pObjs = pFrame->GetDrawObjs())
+ { // also invalidate position of existing flys
+ for (auto const pObject : *pObjs)
+ {
+ pObject->InvalidateObjPos();
+ }
+ }
+ // SwFlyAtContentFrame::SwClientNotify() always appends to
+ // the master frame, so do the same here.
+ // (RemoveFootnotesForNode must be called at least once)
+ if (!pFrame->IsFollow())
+ {
+ // the new text frames don't exist yet, so at this point
+ // we can only delete the footnote frames so they don't
+ // point to the merged SwTextFrame any more...
+ assert(&rTextNode == pMergedPara->pFirstNode);
+ // iterate over nodes, not extents: if a node has
+ // no extents now but did have extents initially,
+ // its flys need their frames deleted too!
+ for (SwNodeOffset j = rTextNode.GetIndex() + 1;
+ j <= pMergedPara->pLastNode->GetIndex(); ++j)
+ {
+ SwNode *const pNode(rTextNode.GetNodes()[j]);
+ assert(!pNode->IsEndNode());
+ if (pNode->IsStartNode())
+ {
+ j = pNode->EndOfSectionIndex();
+ }
+ else if (pNode->IsTextNode())
+ {
+ sw::RemoveFootnotesForNode(rLayout, *pNode->GetTextNode(), nullptr);
+ // similarly, remove the anchored flys
+ for (SwFrameFormat * pFormat : pNode->GetAnchoredFlys())
+ {
+ pFormat->DelFrames(/*&rLayout*/);
+ }
+ }
+ }
+ // rely on AppendAllObjs call at the end to add
+ // all flys in first node that are hidden
+ }
+ pFrame->SetMergedPara(nullptr);
+ }
+ }
+ pFrame->Broadcast(SfxHint()); // notify SwAccessibleParagraph
+ }
+ // all nodes, not just merged ones! it may be in the same list as
+ if (rTextNode.IsNumbered(nullptr)) // a preceding merged one...
+ { // notify frames so they reformat numbering portions
+ rTextNode.NumRuleChgd();
+ }
+ }
+ else if (rNode.IsTableNode() && rLayout.IsHideRedlines())
+ {
+ SwTableNode * pTableNd = rNode.GetTableNode();
+ SwPosition const tmp(rNode);
+ SwRangeRedline const*const pRedline(
+ rLayout.GetFormat()->GetDoc()->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() == &rNode)
+ {
+ for (SwNodeOffset j = rNode.GetIndex(); j <= rNode.EndOfSectionIndex(); ++j)
+ {
+ rNode.GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
+ }
+ pTableNd->DelFrames(&rLayout);
+ }
+ else if ( pTableNd->GetTable().HasDeletedRow() )
+ {
+ pTableNd->DelFrames(&rLayout);
+ if ( !pTableNd->GetTable().IsDeleted() )
+ {
+ SwNodeIndex aIdx( *pTableNd->EndOfSectionNode(), 1 );
+ pTableNd->MakeOwnFrames(&aIdx);
+ }
+ }
+ }
+ else if (rNode.IsTableNode() && !rLayout.IsHideRedlines() &&
+ rNode.GetTableNode()->GetTable().HasDeletedRow() )
+ {
+ SwTableNode * pTableNd = rNode.GetTableNode();
+ pTableNd->DelFrames(&rLayout);
+ SwNodeIndex aIdx( *pTableNd->EndOfSectionNode(), 1 );
+ pTableNd->MakeOwnFrames(&aIdx);
+ }
+
+ if (!rNode.IsCreateFrameWhenHidingRedlines())
+ {
+ if (rLayout.HasMergedParas())
+ {
+ if (rNode.IsContentNode())
+ {
+ // note: nothing to do here, already done
+#ifndef NDEBUG
+ auto const pFrame(static_cast<SwContentNode&>(rNode).getLayoutFrame(&rLayout));
+ assert(!pFrame || static_cast<SwTextFrame*>(pFrame)->GetMergedPara()->pFirstNode != &rNode);
+#endif
+ }
+ }
+ else
+ {
+ assert(!rNode.IsContentNode() || !rNode.GetContentNode()->getLayoutFrame(&rLayout) ||
+ // FIXME: skip this assert in tables with deleted rows
+ rNode.GetContentNode()->getLayoutFrame(&rLayout)->IsInTab());
+ SwNodeOffset j = i + 1;
+ for ( ; j < rEndOfSectionNode.GetIndex(); ++j)
+ {
+ if (rNodes[j]->IsCreateFrameWhenHidingRedlines())
+ {
+ break;
+ }
+ }
+ // call MakeFrames once, because sections/tables
+ // InsertCnt_ also checks for hidden sections
+ SwNodeIndex const start(rNodes, i);
+ SwNodeIndex const end(rNodes, j);
+ {
+ sw::FlyCreationSuppressor aSuppressor(false);
+ ::MakeFrames(rLayout.GetFormat()->GetDoc(), start, end);
+ }
+ i = j - 1; // will be incremented again
+ }
+ }
+ }
+}
+
+static void UnHideRedlinesExtras(SwRootFrame & rLayout,
+ SwNodes & rNodes, SwNode const& rEndOfExtraSectionNode,
+ std::set<SwNodeOffset> *const pSkipped)
+{
+ assert(rEndOfExtraSectionNode.IsEndNode());
+ for (SwNodeOffset i = rEndOfExtraSectionNode.StartOfSectionNode()->GetIndex()
+ + 1; i < rEndOfExtraSectionNode.GetIndex(); ++i)
+ {
+ SwNode const& rStartNode(*rNodes[i]);
+ assert(rStartNode.IsStartNode());
+ assert(rStartNode.GetRedlineMergeFlag() == SwNode::Merge::None);
+ SwNode const& rEndNode(*rStartNode.EndOfSectionNode());
+ bool bSkip(pSkipped && pSkipped->find(i) != pSkipped->end());
+ i = rEndNode.GetIndex();
+ for (SwNodeOffset j = rStartNode.GetIndex() + 1; j < i; ++j)
+ {
+ // note: SwStartNode has no way to access the frames, so check
+ // whether the first content-node inside the section has frames
+ SwNode const& rNode(*rNodes[j]);
+ if (rNode.IsSectionNode() &&
+ static_cast<SwSectionNode const&>(rNode).GetSection().IsHiddenFlag())
+ { // skip hidden sections - they can be inserted in fly-frames :(
+ j = rNode.EndOfSectionNode()->GetIndex();
+ continue;
+ }
+ if (rNode.IsContentNode())
+ {
+ SwContentNode const& rCNode(static_cast<SwContentNode const&>(rNode));
+ if (!rCNode.getLayoutFrame(&rLayout))
+ { // ignore footnote/fly/header/footer with no layout frame
+ bSkip = true; // they will be created from scratch later if needed
+ }
+ break;
+ }
+ }
+ if (!bSkip)
+ {
+ UnHideRedlines(rLayout, rNodes, rEndNode, pSkipped);
+ }
+ }
+}
+
+static void UnHide(SwRootFrame & rLayout)
+{
+ assert(rLayout.GetCurrShell()->ActionPend()); // tdf#125754 avoid recursive layout
+ SwDoc & rDoc(*rLayout.GetFormat()->GetDoc());
+ // don't do early return if there are no redlines:
+ // Show->Hide must init hidden number trees
+ // Hide->Show may be called after all redlines have been deleted but there
+ // may still be MergedParas because those aren't deleted yet...
+#if 0
+ if (!bHideRedlines
+ && rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ return;
+ }
+#endif
+ // Hide->Show: clear MergedPara, create frames
+ // Show->Hide: call CheckParaRedlineMerge, delete frames
+ // Traverse the document via the nodes-array; traversing via the layout
+ // wouldn't find the nodes that don't have frames in the ->Show case.
+ // In-order traversal of each nodes array section should init the flags
+ // in nodes before they are iterated.
+ // Actual creation of frames should be done with existing functions
+ // if possible, particularly InsertCnt_() or its wrapper ::MakeFrames().
+ SwNodes /*const*/& rNodes(rDoc.GetNodes());
+ // Flys/footnotes: must iterate and find all the ones that already exist
+ // with frames and have redlines inside them; if any don't have frames at
+ // all, they will be created (if necessary) from scratch and completely by
+ // MakeFrames().
+ //
+ // Flys before footnotes: because footnotes may contain flys but not
+ // vice-versa; alas flys may contain flys, so we skip some of them
+ // if they have already been created from scratch via their anchor flys.
+ std::set<SwNodeOffset> skippedFlys;
+ UnHideRedlinesExtras(rLayout, rNodes, rNodes.GetEndOfAutotext(),
+ // when un-hiding, delay all fly frame creation to AppendAllObjs below
+ rLayout.HasMergedParas() ? &skippedFlys : nullptr);
+ // Footnotes are created automatically (after invalidation etc.) by
+ // ConnectFootnote(), but need to be deleted manually. Footnotes do not
+ // occur in flys or headers/footers.
+ UnHideRedlinesExtras(rLayout, rNodes, rNodes.GetEndOfInserts(), nullptr);
+ UnHideRedlines(rLayout, rNodes, rNodes.GetEndOfContent(), nullptr);
+
+ if (!rLayout.HasMergedParas())
+ { // create all previously hidden flys at once:
+ // * Flys on first node of pre-existing merged frames that are hidden
+ // (in delete redline), to be added to the existing frame
+ // * Flys on non-first (hidden/merged) nodes of pre-existing merged
+ // frames, to be added to the new frame of their node
+ // * Flys anchored in other flys that are hidden
+ AppendAllObjs(rDoc.GetSpzFrameFormats(), &rLayout);
+ }
+
+ const bool bIsShowChangesInMargin = rLayout.GetCurrShell()->GetViewOptions()->IsShowChangesInMargin();
+ for (auto const pRedline : rDoc.getIDocumentRedlineAccess().GetRedlineTable())
+ { // DELETE are handled by the code above; for other types, need to
+ // trigger repaint of text frames to add/remove the redline color font
+ // (handle deletions showed in margin also here)
+ if (bIsShowChangesInMargin || pRedline->GetType() != RedlineType::Delete)
+ {
+ pRedline->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+ }
+
+ SwFootnoteIdxs & rFootnotes(rDoc.GetFootnoteIdxs());
+ if (rDoc.GetFootnoteInfo().m_eNum == FTNNUM_CHAPTER)
+ {
+ // sadly determining which node is outline node requires hidden layout
+ rFootnotes.UpdateAllFootnote();
+ }
+ // invalidate all footnotes to reformat their numbers
+ for (SwTextFootnote *const pFootnote : rFootnotes)
+ {
+ SwFormatFootnote const& rFootnote(pFootnote->GetFootnote());
+ if (rFootnote.GetNumber() != rFootnote.GetNumberRLHidden()
+ && rFootnote.GetNumStr().isEmpty())
+ {
+ pFootnote->InvalidateNumberInLayout();
+ }
+ }
+ // update various fields to re-expand them with the new layout
+ IDocumentFieldsAccess & rIDFA(rDoc.getIDocumentFieldsAccess());
+ auto const pAuthType(rIDFA.GetFieldType(
+ SwFieldIds::TableOfAuthorities, OUString(), false));
+ if (pAuthType) // created on demand...
+ { // calling DelSequenceArray() should be unnecessary here since the
+ // sequence doesn't depend on frames
+ pAuthType->UpdateFields();
+ }
+ rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
+ rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
+ rIDFA.UpdateExpFields(nullptr, false);
+ rIDFA.UpdateRefFields();
+
+ // update SwPostItMgr / notes in the margin
+ // note: as long as all shells share layout, broadcast to all shells!
+ rDoc.GetDocShell()->Broadcast( SwFormatFieldHint(nullptr, rLayout.HasMergedParas()
+ ? SwFormatFieldHintWhich::REMOVED
+ : SwFormatFieldHintWhich::INSERTED) );
+
+
+// InvalidateAllContent(SwInvalidateFlags::Size); // ??? TODO what to invalidate? this is the big hammer
+}
+
+void SwRootFrame::SetHideRedlines(bool const bHideRedlines)
+{
+ if (bHideRedlines == mbHideRedlines)
+ {
+ return;
+ }
+ // TODO: remove temporary ShowBoth
+ sw::FieldmarkMode const eMode(m_FieldmarkMode);
+ if (HasMergedParas())
+ {
+ m_FieldmarkMode = sw::FieldmarkMode::ShowBoth;
+ mbHideRedlines = false;
+ UnHide(*this);
+ }
+ if (bHideRedlines || eMode != m_FieldmarkMode)
+ {
+ m_FieldmarkMode = eMode;
+ mbHideRedlines = bHideRedlines;
+ UnHide(*this);
+ }
+}
+
+void SwRootFrame::SetFieldmarkMode(sw::FieldmarkMode const eMode)
+{
+ if (eMode == m_FieldmarkMode)
+ {
+ return;
+ }
+ // TODO: remove temporary ShowBoth
+ bool const isHideRedlines(mbHideRedlines);
+ if (HasMergedParas())
+ {
+ mbHideRedlines = false;
+ m_FieldmarkMode = sw::FieldmarkMode::ShowBoth;
+ UnHide(*this);
+ }
+ if (eMode != sw::FieldmarkMode::ShowBoth || isHideRedlines)
+ {
+ mbHideRedlines = isHideRedlines;
+ m_FieldmarkMode = eMode;
+ UnHide(*this);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */