diff options
Diffstat (limited to 'sw/source/core/objectpositioning')
5 files changed, 3204 insertions, 0 deletions
diff --git a/sw/source/core/objectpositioning/anchoredobjectposition.cxx b/sw/source/core/objectpositioning/anchoredobjectposition.cxx new file mode 100644 index 0000000000..e530e36df3 --- /dev/null +++ b/sw/source/core/objectpositioning/anchoredobjectposition.cxx @@ -0,0 +1,1146 @@ +/* -*- 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 <anchoredobjectposition.hxx> +#include <environmentofanchoredobject.hxx> +#include <flyfrm.hxx> +#include <flyfrms.hxx> +#include <txtfrm.hxx> +#include <pagefrm.hxx> +#include <frmatr.hxx> +#include <frmtool.hxx> +#include <svx/svdobj.hxx> +#include <dflyobj.hxx> +#include <dcontact.hxx> +#include <frmfmt.hxx> +#include <fmtornt.hxx> +#include <fmtfollowtextflow.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <ndtxt.hxx> +#include <IDocumentSettingAccess.hxx> +#include <textboxhelper.hxx> +#include <fmtsrnd.hxx> +#include <osl/diagnose.h> + +using namespace ::com::sun::star; + +namespace objectpositioning +{ + +SwAnchoredObjectPosition::SwAnchoredObjectPosition( SdrObject& _rDrawObj ) + : mrDrawObj( _rDrawObj ), + mpAnchoredObj( nullptr ), + mpAnchorFrame( nullptr ), + mpContact( nullptr ), + mbIsObjFly( false ), + // #i62875# + mbFollowTextFlow( false ), + mbDoNotCaptureAnchoredObj( false ) +{ +#if OSL_DEBUG_LEVEL > 0 + // assert, if object isn't of expected type + const bool bObjOfExceptedType = + dynamic_cast<const SwVirtFlyDrawObj*>( &mrDrawObj) != nullptr || // object representing fly frame + dynamic_cast<const SwDrawVirtObj*>( &mrDrawObj) != nullptr || // 'virtual' drawing object + ( dynamic_cast<const SdrVirtObj*>( &mrDrawObj) == nullptr && // 'master' drawing object + dynamic_cast<const SwFlyDrawObj*>( &mrDrawObj) == nullptr ); // - indirectly checked + OSL_ENSURE( bObjOfExceptedType, + "SwAnchoredObjectPosition(..) - object of unexpected type!" ); +#endif + + GetInfoAboutObj(); +} + +/** determine information about object + + members <mbIsObjFly>, <mpFrameOfObj>, <mpAnchorFrame>, <mpContact>, + <mbFollowTextFlow> and <mbDoNotCaptureAnchoredObj> are set +*/ +void SwAnchoredObjectPosition::GetInfoAboutObj() +{ + // determine, if object represents a fly frame + { + mbIsObjFly = dynamic_cast<const SwVirtFlyDrawObj*>( &mrDrawObj) != nullptr; + } + + // determine contact object + { + mpContact = GetUserCall( &mrDrawObj ); + assert(mpContact && + "SwAnchoredObjectPosition::GetInfoAboutObj() - missing SwContact-object."); + } + + // determine anchored object, the object belongs to + { + // #i26791# + mpAnchoredObj = mpContact->GetAnchoredObj( &mrDrawObj ); + assert(mpAnchoredObj && + "SwAnchoredObjectPosition::GetInfoAboutObj() - missing anchored object."); + } + + // determine frame, the object is anchored at + { + // #i26791# + mpAnchorFrame = mpAnchoredObj->AnchorFrame(); + OSL_ENSURE( mpAnchorFrame, + "SwAnchoredObjectPosition::GetInfoAboutObj() - missing anchor frame." ); + } + + // determine format the object belongs to + { + // #i28701# + mpFrameFormat = &mpAnchoredObj->GetFrameFormat(); + assert(mpFrameFormat && + "<SwAnchoredObjectPosition::GetInfoAboutObj() - missing frame format."); + } + + // #i62875# - determine attribute value of <Follow-Text-Flow> + { + mbFollowTextFlow = mpFrameFormat->GetFollowTextFlow().GetValue(); + } + + // determine, if anchored object has not to be captured on the page. + // the following conditions must be hold to *not* capture it: + // - corresponding document compatibility flag is set + // - it's a drawing object or it's a non-textbox wrap-though fly frame + // - it doesn't follow the text flow + { + bool bTextBox = SwTextBoxHelper::isTextBox(mpFrameFormat, RES_FLYFRMFMT); + bool bWrapThrough = mpFrameFormat->GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH; + mbDoNotCaptureAnchoredObj = (!mbIsObjFly || (!bTextBox && bWrapThrough)) && !mbFollowTextFlow && + mpFrameFormat->getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE); + } +} + +SwAnchoredObjectPosition::~SwAnchoredObjectPosition() +{} + +bool SwAnchoredObjectPosition::IsAnchoredToChar() const +{ + return false; +} + +const SwFrame* SwAnchoredObjectPosition::ToCharOrientFrame() const +{ + return nullptr; +} + +const SwRect* SwAnchoredObjectPosition::ToCharRect() const +{ + return nullptr; +} + +// #i22341# +SwTwips SwAnchoredObjectPosition::ToCharTopOfLine() const +{ + return 0; +} + +/** helper method to determine top of a frame for the vertical + object positioning + + #i11860# +*/ +SwTwips SwAnchoredObjectPosition::GetTopForObjPos( const SwFrame& _rFrame, + const SwRectFn& _fnRect, + const bool _bVert ) const +{ + SwTwips nTopOfFrameForObjPos = (_rFrame.getFrameArea().*_fnRect->fnGetTop)(); + + if ( _rFrame.IsTextFrame() ) + { + const SwTextFrame& rTextFrame = static_cast<const SwTextFrame&>(_rFrame); + if ( _bVert ) + { + nTopOfFrameForObjPos -= + rTextFrame.GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid(); + } + else + { + nTopOfFrameForObjPos += + rTextFrame.GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid(); + + const SwFormatSurround& rSurround = mpFrameFormat->GetSurround(); + bool bWrapThrough = rSurround.GetSurround() == css::text::WrapTextMode_THROUGH; + // If the frame format is a TextBox of a draw shape, then use the + // surround of the original shape. + SwTextBoxHelper::getShapeWrapThrough(mpFrameFormat, bWrapThrough); + + // Get the offset between the top of the text frame and the top of + // the first line inside the frame that has more than just fly + // portions. + nTopOfFrameForObjPos += rTextFrame.GetBaseVertOffsetForFly(!bWrapThrough); + } + } + + return nTopOfFrameForObjPos; +} + +void SwAnchoredObjectPosition::GetVertAlignmentValues( + const SwFrame& _rVertOrientFrame, + const SwFrame& _rPageAlignLayFrame, + const sal_Int16 _eRelOrient, + SwTwips& _orAlignAreaHeight, + SwTwips& _orAlignAreaOffset ) const +{ + SwTwips nHeight = 0; + SwTwips nOffset = 0; + SwRectFnSet aRectFnSet(&_rVertOrientFrame); + // #i11860# - top of <_rVertOrientFrame> for object positioning + const SwTwips nVertOrientTop = GetTopForObjPos( _rVertOrientFrame, aRectFnSet.FnRect(), aRectFnSet.IsVert() ); + // #i11860# - upper space amount of <_rVertOrientFrame> considered + // for previous frame + const SwTwips nVertOrientUpperSpaceForPrevFrameAndPageGrid = + _rVertOrientFrame.IsTextFrame() + ? static_cast<const SwTextFrame&>(_rVertOrientFrame). + GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid() + : 0; + switch ( _eRelOrient ) + { + case text::RelOrientation::FRAME: + { + // #i11860# - consider upper space of previous frame + nHeight = aRectFnSet.GetHeight(_rVertOrientFrame.getFrameArea()) - + nVertOrientUpperSpaceForPrevFrameAndPageGrid; + nOffset = 0; + } + break; + case text::RelOrientation::PRINT_AREA: + { + nHeight = aRectFnSet.GetHeight(_rVertOrientFrame.getFramePrintArea()); + // #i11860# - consider upper space of previous frame + nOffset = aRectFnSet.GetTopMargin(_rVertOrientFrame) - + nVertOrientUpperSpaceForPrevFrameAndPageGrid; + // if aligned to page in horizontal layout, consider header and + // footer frame height appropriately. + if( _rVertOrientFrame.IsPageFrame() && !aRectFnSet.IsVert() ) + { + const SwFrame* pPrtFrame = + static_cast<const SwPageFrame&>(_rVertOrientFrame).Lower(); + while( pPrtFrame ) + { + if( pPrtFrame->IsHeaderFrame() ) + { + nHeight -= pPrtFrame->getFrameArea().Height(); + nOffset += pPrtFrame->getFrameArea().Height(); + } + else if( pPrtFrame->IsFooterFrame() ) + { + nHeight -= pPrtFrame->getFrameArea().Height(); + } + pPrtFrame = pPrtFrame->GetNext(); + } + } + } + break; + case text::RelOrientation::PAGE_FRAME: + case text::RelOrientation::PAGE_PRINT_AREA_TOP: + { + nHeight = aRectFnSet.GetHeight(_rPageAlignLayFrame.getFrameArea()); + nOffset = aRectFnSet.YDiff( + aRectFnSet.GetTop(_rPageAlignLayFrame.getFrameArea()), + nVertOrientTop ); + } + break; + case text::RelOrientation::PAGE_PRINT_AREA: + { + nHeight = aRectFnSet.GetHeight(_rPageAlignLayFrame.getFramePrintArea()); + nOffset = aRectFnSet.GetTopMargin(_rPageAlignLayFrame) + + aRectFnSet.YDiff( + aRectFnSet.GetTop(_rPageAlignLayFrame.getFrameArea()), + nVertOrientTop ); + // if aligned to page in horizontal layout, consider header and + // footer frame height appropriately. + if( _rPageAlignLayFrame.IsPageFrame() && !aRectFnSet.IsVert() ) + { + const SwFrame* pPrtFrame = + static_cast<const SwPageFrame&>(_rPageAlignLayFrame).Lower(); + while( pPrtFrame ) + { + if( pPrtFrame->IsHeaderFrame() ) + { + nHeight -= pPrtFrame->getFrameArea().Height(); + nOffset += pPrtFrame->getFrameArea().Height(); + } + else if( pPrtFrame->IsFooterFrame() ) + { + nHeight -= pPrtFrame->getFrameArea().Height(); + } + pPrtFrame = pPrtFrame->GetNext(); + } + } + } + break; + case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM: + { + nHeight = aRectFnSet.GetBottomMargin(_rPageAlignLayFrame); + nOffset = aRectFnSet.YDiff( + aRectFnSet.GetPrtBottom(_rPageAlignLayFrame), + nVertOrientTop); + + if (_rPageAlignLayFrame.IsPageFrame() && !aRectFnSet.IsVert()) + { + const SwFrame* pPrtFrame = + static_cast<const SwPageFrame&>(_rPageAlignLayFrame).Lower(); + + while (pPrtFrame) + { + if (pPrtFrame->IsFooterFrame()) + { + nHeight += pPrtFrame->getFrameArea().Height(); + nOffset -= pPrtFrame->getFrameArea().Height(); + } + pPrtFrame = pPrtFrame->GetNext(); + } + } + + } + break; + // #i22341# - vertical alignment at top of line + case text::RelOrientation::TEXT_LINE: + { + if ( IsAnchoredToChar() ) + { + nHeight = 0; + nOffset = aRectFnSet.YDiff( ToCharTopOfLine(), nVertOrientTop ); + } + else + { + OSL_FAIL( "<SwAnchoredObjectPosition::GetVertAlignmentValues(..)> - invalid relative alignment" ); + } + } + break; + case text::RelOrientation::CHAR: + { + if ( IsAnchoredToChar() ) + { + nHeight = aRectFnSet.GetHeight(*ToCharRect()); + nOffset = aRectFnSet.YDiff( aRectFnSet.GetTop(*ToCharRect()), + nVertOrientTop ); + } + else + { + OSL_FAIL( "<SwAnchoredObjectPosition::GetVertAlignmentValues(..)> - invalid relative alignment" ); + } + } + break; + // no break here, because text::RelOrientation::CHAR is invalid, if !mbAnchorToChar + default: + { + OSL_FAIL( "<SwAnchoredObjectPosition::GetVertAlignmentValues(..)> - invalid relative alignment" ); + } + } + + _orAlignAreaHeight = nHeight; + _orAlignAreaOffset = nOffset; +} + +// #i26791# - add output parameter <_roVertOffsetToFrameAnchorPos> +SwTwips SwAnchoredObjectPosition::GetVertRelPos( + const SwFrame& _rVertOrientFrame, + const SwFrame& _rPageAlignLayFrame, + const sal_Int16 _eVertOrient, + const sal_Int16 _eRelOrient, + const SwTwips _nVertPos, + const SvxLRSpaceItem& _rLRSpacing, + const SvxULSpaceItem& _rULSpacing, + SwTwips& _roVertOffsetToFrameAnchorPos ) const +{ + SwTwips nRelPosY = 0; + SwRectFnSet aRectFnSet(&_rVertOrientFrame); + + SwTwips nAlignAreaHeight; + SwTwips nAlignAreaOffset; + GetVertAlignmentValues( _rVertOrientFrame, _rPageAlignLayFrame, + _eRelOrient, nAlignAreaHeight, nAlignAreaOffset ); + + nRelPosY = nAlignAreaOffset; + const SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() ); + const SwTwips nObjHeight = aRectFnSet.GetHeight(aObjBoundRect); + + switch ( _eVertOrient ) + { + case text::VertOrientation::NONE: + { + // 'manual' vertical position + nRelPosY += _nVertPos; + } + break; + case text::VertOrientation::TOP: + { + nRelPosY += aRectFnSet.IsVert() + ? ( aRectFnSet.IsVertL2R() + ? _rLRSpacing.GetLeft() + : _rLRSpacing.GetRight() ) + : _rULSpacing.GetUpper(); + } + break; + case text::VertOrientation::CENTER: + { + nRelPosY += (nAlignAreaHeight / 2) - (nObjHeight / 2); + } + break; + case text::VertOrientation::BOTTOM: + { + nRelPosY += nAlignAreaHeight - + ( nObjHeight + ( aRectFnSet.IsVert() + ? ( aRectFnSet.IsVertL2R() + ? _rLRSpacing.GetRight() + : _rLRSpacing.GetLeft() ) + : _rULSpacing.GetLower() ) ); + } + break; + default: + { + OSL_FAIL( "<SwAnchoredObjectPosition::GetVertRelPos(..) - invalid vertical positioning" ); + } + } + + // #i26791# + _roVertOffsetToFrameAnchorPos = nAlignAreaOffset; + + return nRelPosY; +} + +/** adjust calculated vertical in order to keep object inside + 'page' alignment layout frame. + + #i28701# - parameter <_nTopOfAnch> and <_bVert> added + #i31805# - add parameter <_bCheckBottom> + #i26945# - add parameter <_bFollowTextFlow> + #i62875# - method now private and renamed. + OD 2009-09-01 #mongolianlayout# - add parameter <bVertL2R> +*/ +SwTwips SwAnchoredObjectPosition::ImplAdjustVertRelPos( const SwTwips nTopOfAnch, + const bool bVert, + const bool bVertL2R, + const SwFrame& rPageAlignLayFrame, + const SwTwips nProposedRelPosY, + const bool bFollowTextFlow, + const bool bCheckBottom ) const +{ + SwTwips nAdjustedRelPosY = nProposedRelPosY; + // TODO: Replace the following condition with the correction + // of the implementation of option FollowTextFlow. + if ( SwAnchoredObject::IsDraggingOffPageAllowed(FindFrameFormat(&mrDrawObj)) && + !(GetAnchorFrame().IsInTab() && DoesObjFollowsTextFlow()) ) + { + return nAdjustedRelPosY; + } + + const Size aObjSize(GetAnchoredObj().GetObjRect().SSize()); + // determine the area of 'page' alignment frame, to which the vertical + // position is restricted. + // #i28701# - Extend restricted area for the vertical + // position to area of the page frame, if wrapping style influence is + // considered on object positioning. Needed to avoid layout loops in the + // object positioning algorithm considering the wrapping style influence + // caused by objects, which follow the text flow and thus are restricted + // to its environment (e.g. page header/footer). + SwRect aPgAlignArea; + { + // #i26945# - no extension of restricted area, if + // object's attribute follow text flow is set and its inside a table + if ( GetFrameFormat().getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) && + ( !bFollowTextFlow || + !GetAnchoredObj().GetAnchorFrame()->IsInTab() ) ) + { + aPgAlignArea = rPageAlignLayFrame.FindPageFrame()->getFrameArea(); + } + else + { + aPgAlignArea = rPageAlignLayFrame.getFrameArea(); + } + } + + if ( bVert ) + { + // #i31805# - consider value of <_bCheckBottom> + if ( !bVertL2R ) + { + if ( bCheckBottom && + nTopOfAnch - nAdjustedRelPosY - aObjSize.Width() < + aPgAlignArea.Left() ) + { + nAdjustedRelPosY = aPgAlignArea.Left() + + nTopOfAnch - + aObjSize.Width(); + } + // #i32964# - correction + if ( nTopOfAnch - nAdjustedRelPosY > aPgAlignArea.Right() ) + { + nAdjustedRelPosY = nTopOfAnch - aPgAlignArea.Right(); + } + } + else + { + // tdf#112443 if position is completely off-page + // return the proposed position and do not adjust it... + // tdf#120839 .. unless anchored to char (anchor can jump on other page) + const bool bDisablePositioning = mpFrameFormat->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING); + if ( bDisablePositioning && !IsAnchoredToChar() && nTopOfAnch + nAdjustedRelPosY > aPgAlignArea.Right() ) + { + return nProposedRelPosY; + } + + if ( bCheckBottom && + nTopOfAnch + nAdjustedRelPosY + aObjSize.Width() > + aPgAlignArea.Right() ) + { + nAdjustedRelPosY = aPgAlignArea.Right() - + nTopOfAnch - + aObjSize.Width(); + } + if ( nTopOfAnch + nAdjustedRelPosY < aPgAlignArea.Left() ) + { + nAdjustedRelPosY = aPgAlignArea.Left() - nTopOfAnch; + } + } + } + else + { + // tdf#112443 if position is completely off-page + // return the proposed position and do not adjust it... + const bool bDisablePositioning = mpFrameFormat->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING); + + // tdf#123002 disable the positioning in header and footer only + // we should limit this since anchors of body frames may appear on other pages + const bool bIsFooterOrHeader = GetAnchorFrame().GetUpper() + && (GetAnchorFrame().GetUpper()->IsFooterFrame() || GetAnchorFrame().GetUpper()->IsHeaderFrame() ); + + if ( bDisablePositioning && bIsFooterOrHeader && nTopOfAnch + nAdjustedRelPosY > aPgAlignArea.Bottom() ) + { + return nProposedRelPosY; + } + + // #i31805# - consider value of <bCheckBottom> + if ( bCheckBottom && + nTopOfAnch + nAdjustedRelPosY + aObjSize.Height() > + aPgAlignArea.Top() + aPgAlignArea.Height() ) + { + nAdjustedRelPosY = aPgAlignArea.Top() + aPgAlignArea.Height() - + nTopOfAnch - + aObjSize.Height(); + } + if ( nTopOfAnch + nAdjustedRelPosY < aPgAlignArea.Top() ) + { + nAdjustedRelPosY = aPgAlignArea.Top() - nTopOfAnch; + } + } + return nAdjustedRelPosY; +} + +/** adjust calculated horizontal in order to keep object inside + 'page' alignment layout frame. + + #i62875# - method now private and renamed. +*/ +SwTwips SwAnchoredObjectPosition::ImplAdjustHoriRelPos( + const SwFrame& _rPageAlignLayFrame, + const SwTwips _nProposedRelPosX ) const +{ + SwTwips nAdjustedRelPosX = _nProposedRelPosX; + + if (SwAnchoredObject::IsDraggingOffPageAllowed(FindFrameFormat(&mrDrawObj))) + return nAdjustedRelPosX; + + const SwFrame& rAnchorFrame = GetAnchorFrame(); + const bool bVert = rAnchorFrame.IsVertical(); + + const Size aObjSize( GetAnchoredObj().GetObjRect().SSize() ); + + if( bVert ) + { + if ( rAnchorFrame.getFrameArea().Top() + nAdjustedRelPosX + aObjSize.Height() > + _rPageAlignLayFrame.getFrameArea().Bottom() ) + { + nAdjustedRelPosX = _rPageAlignLayFrame.getFrameArea().Bottom() - + rAnchorFrame.getFrameArea().Top() - + aObjSize.Height(); + } + if ( rAnchorFrame.getFrameArea().Top() + nAdjustedRelPosX < + _rPageAlignLayFrame.getFrameArea().Top() ) + { + nAdjustedRelPosX = _rPageAlignLayFrame.getFrameArea().Top() - + rAnchorFrame.getFrameArea().Top(); + } + } + else + { + if ( rAnchorFrame.getFrameArea().Left() + nAdjustedRelPosX + aObjSize.Width() > + _rPageAlignLayFrame.getFrameArea().Right() ) + { + nAdjustedRelPosX = _rPageAlignLayFrame.getFrameArea().Right() - + rAnchorFrame.getFrameArea().Left() - + aObjSize.Width(); + } + if ( rAnchorFrame.getFrameArea().Left() + nAdjustedRelPosX < + _rPageAlignLayFrame.getFrameArea().Left() ) + { + nAdjustedRelPosX = _rPageAlignLayFrame.getFrameArea().Left() - + rAnchorFrame.getFrameArea().Left(); + } + } + + return nAdjustedRelPosX; +} + +/** determine alignment value for horizontal position of object */ +void SwAnchoredObjectPosition::GetHoriAlignmentValues( const SwFrame& _rHoriOrientFrame, + const SwFrame& _rPageAlignLayFrame, + const sal_Int16 _eRelOrient, + const bool _bObjWrapThrough, + SwTwips& _orAlignAreaWidth, + SwTwips& _orAlignAreaOffset, + bool& _obAlignedRelToPage ) const +{ + SwTwips nWidth = 0; + SwTwips nOffset = 0; + SwRectFnSet aRectFnSet(&_rHoriOrientFrame); + switch ( _eRelOrient ) + { + case text::RelOrientation::PRINT_AREA: + { + nWidth = aRectFnSet.GetWidth(_rHoriOrientFrame.getFramePrintArea()); + nOffset = aRectFnSet.GetLeftMargin(_rHoriOrientFrame); + if ( _rHoriOrientFrame.IsTextFrame() ) + { + // consider movement of text frame left + nOffset += static_cast<const SwTextFrame&>(_rHoriOrientFrame).GetBaseOffsetForFly( !_bObjWrapThrough ); + } + else if ( _rHoriOrientFrame.IsPageFrame() && aRectFnSet.IsVert() ) + { + // for to-page anchored objects, consider header/footer frame + // in vertical layout + const SwFrame* pPrtFrame = + static_cast<const SwPageFrame&>(_rHoriOrientFrame).Lower(); + while( pPrtFrame ) + { + if( pPrtFrame->IsHeaderFrame() ) + { + nWidth -= pPrtFrame->getFrameArea().Height(); + nOffset += pPrtFrame->getFrameArea().Height(); + } + else if( pPrtFrame->IsFooterFrame() ) + { + nWidth -= pPrtFrame->getFrameArea().Height(); + } + pPrtFrame = pPrtFrame->GetNext(); + } + } + break; + } + case text::RelOrientation::PAGE_LEFT: + { + // align at left border of page frame/fly frame/cell frame + nWidth = aRectFnSet.GetLeftMargin(_rPageAlignLayFrame); + nOffset = aRectFnSet.XDiff( + aRectFnSet.GetLeft(_rPageAlignLayFrame.getFrameArea()), + aRectFnSet.GetLeft(_rHoriOrientFrame.getFrameArea()) ); + _obAlignedRelToPage = true; + } + break; + case text::RelOrientation::PAGE_RIGHT: + { + // align at right border of page frame/fly frame/cell frame + nWidth = aRectFnSet.GetRightMargin(_rPageAlignLayFrame); + nOffset = aRectFnSet.XDiff( + aRectFnSet.GetPrtRight(_rPageAlignLayFrame), + aRectFnSet.GetLeft(_rHoriOrientFrame.getFrameArea()) ); + _obAlignedRelToPage = true; + } + break; + case text::RelOrientation::FRAME_LEFT: + { + // align at left border of anchor frame + nWidth = aRectFnSet.GetLeftMargin(_rHoriOrientFrame); + nOffset = 0; + } + break; + case text::RelOrientation::FRAME_RIGHT: + { + // align at right border of anchor frame + // Unify and simplify + nWidth = aRectFnSet.GetRightMargin(_rHoriOrientFrame); + nOffset = aRectFnSet.GetRight(_rHoriOrientFrame.getFramePrintArea()); + } + break; + case text::RelOrientation::CHAR: + { + // alignment relative to character - assure, that corresponding + // character rectangle is set. + if ( IsAnchoredToChar() ) + { + nWidth = 0; + nOffset = aRectFnSet.XDiff( + aRectFnSet.GetLeft(*ToCharRect()), + aRectFnSet.GetLeft(ToCharOrientFrame()->getFrameArea()) ); + break; + } + [[fallthrough]]; + } + case text::RelOrientation::PAGE_PRINT_AREA: + { + nWidth = aRectFnSet.GetWidth(_rPageAlignLayFrame.getFramePrintArea()); + nOffset = aRectFnSet.XDiff( + aRectFnSet.GetPrtLeft(_rPageAlignLayFrame), + aRectFnSet.GetLeft(_rHoriOrientFrame.getFrameArea()) ); + if ( _rHoriOrientFrame.IsPageFrame() && aRectFnSet.IsVert() ) + { + // for to-page anchored objects, consider header/footer frame + // in vertical layout + const SwFrame* pPrtFrame = + static_cast<const SwPageFrame&>(_rHoriOrientFrame).Lower(); + while( pPrtFrame ) + { + if( pPrtFrame->IsHeaderFrame() ) + { + nWidth -= pPrtFrame->getFrameArea().Height(); + nOffset += pPrtFrame->getFrameArea().Height(); + } + else if( pPrtFrame->IsFooterFrame() ) + { + nWidth -= pPrtFrame->getFrameArea().Height(); + } + pPrtFrame = pPrtFrame->GetNext(); + } + } + _obAlignedRelToPage = true; + break; + } + case text::RelOrientation::PAGE_FRAME: + { + nWidth = aRectFnSet.GetWidth(_rPageAlignLayFrame.getFrameArea()); + nOffset = aRectFnSet.XDiff( + aRectFnSet.GetLeft(_rPageAlignLayFrame.getFrameArea()), + aRectFnSet.GetLeft(_rHoriOrientFrame.getFrameArea()) ); + _obAlignedRelToPage = true; + break; + } + default: + { + nWidth = aRectFnSet.GetWidth(_rHoriOrientFrame.getFrameArea()); + + bool bWrapThrough = _bObjWrapThrough; + // If the frame format is a TextBox of a draw shape, then use the + // surround of the original shape. + SwTextBoxHelper::getShapeWrapThrough(mpFrameFormat, bWrapThrough); + + bool bIgnoreFlysAnchoredAtFrame = !bWrapThrough; + nOffset = _rHoriOrientFrame.IsTextFrame() ? + static_cast<const SwTextFrame&>(_rHoriOrientFrame).GetBaseOffsetForFly( bIgnoreFlysAnchoredAtFrame ) : + 0; + break; + } + } + + _orAlignAreaWidth = nWidth; + _orAlignAreaOffset = nOffset; +} + +/** toggle given horizontal orientation and relative alignment */ +void SwAnchoredObjectPosition::ToggleHoriOrientAndAlign( + const bool _bToggleLeftRight, + sal_Int16& _ioeHoriOrient, + sal_Int16& _iopeRelOrient + ) +{ + if( !_bToggleLeftRight ) + return; + + // toggle orientation + switch ( _ioeHoriOrient ) + { + case text::HoriOrientation::RIGHT : + { + _ioeHoriOrient = text::HoriOrientation::LEFT; + } + break; + case text::HoriOrientation::LEFT : + { + _ioeHoriOrient = text::HoriOrientation::RIGHT; + } + break; + default: + break; + } + + // toggle relative alignment + switch ( _iopeRelOrient ) + { + case text::RelOrientation::PAGE_RIGHT : + { + _iopeRelOrient = text::RelOrientation::PAGE_LEFT; + } + break; + case text::RelOrientation::PAGE_LEFT : + { + _iopeRelOrient = text::RelOrientation::PAGE_RIGHT; + } + break; + case text::RelOrientation::FRAME_RIGHT : + { + _iopeRelOrient = text::RelOrientation::FRAME_LEFT; + } + break; + case text::RelOrientation::FRAME_LEFT : + { + _iopeRelOrient = text::RelOrientation::FRAME_RIGHT; + } + break; + default: + break; + } +} + +/** calculate relative horizontal position */ +SwTwips SwAnchoredObjectPosition::CalcRelPosX( + const SwFrame& _rHoriOrientFrame, + const SwEnvironmentOfAnchoredObject& _rEnvOfObj, + const SwFormatHoriOrient& _rHoriOrient, + const SvxLRSpaceItem& _rLRSpacing, + const SvxULSpaceItem& _rULSpacing, + const bool _bObjWrapThrough, + const SwTwips _nRelPosY, + SwTwips& _roHoriOffsetToFrameAnchorPos + ) const +{ + // determine 'page' alignment layout frame + const SwFrame& rPageAlignLayFrame = + _rEnvOfObj.GetHoriEnvironmentLayoutFrame( _rHoriOrientFrame ); + + const bool bEvenPage = !rPageAlignLayFrame.OnRightPage(); + const bool bToggle = _rHoriOrient.IsPosToggle() && bEvenPage; + + // determine orientation and relative alignment + sal_Int16 eHoriOrient = _rHoriOrient.GetHoriOrient(); + sal_Int16 eRelOrient = _rHoriOrient.GetRelationOrient(); + // toggle orientation and relative alignment + ToggleHoriOrientAndAlign( bToggle, eHoriOrient, eRelOrient ); + + // determine alignment parameter + // <nWidth>: 'width' of alignment area + // <nOffset>: offset of alignment area, relative to 'left' of anchor frame + SwTwips nWidth = 0; + SwTwips nOffset = 0; + bool bAlignedRelToPage = false; + GetHoriAlignmentValues( _rHoriOrientFrame, rPageAlignLayFrame, + eRelOrient, _bObjWrapThrough, + nWidth, nOffset, bAlignedRelToPage ); + + const SwFrame& rAnchorFrame = GetAnchorFrame(); + SwRectFnSet aRectFnSet(&_rHoriOrientFrame); + SwTwips nObjWidth = aRectFnSet.GetWidth(GetAnchoredObj().GetObjRect()); + SwTwips nRelPosX = nOffset; + if ( _rHoriOrient.GetHoriOrient() == text::HoriOrientation::NONE ) + { + // 'manual' horizontal position + const bool bR2L = rAnchorFrame.IsRightToLeft(); + if( IsAnchoredToChar() && text::RelOrientation::CHAR == eRelOrient ) + { + if( bR2L ) + nRelPosX -= _rHoriOrient.GetPos(); + else + nRelPosX += _rHoriOrient.GetPos(); + } + else if ( bToggle || ( !_rHoriOrient.IsPosToggle() && bR2L ) ) + { + // Correction: consider <nOffset> also for + // toggling from left to right. + nRelPosX += nWidth - nObjWidth - _rHoriOrient.GetPos(); + } + else + { + nRelPosX += _rHoriOrient.GetPos(); + } + } + else if ( text::HoriOrientation::CENTER == eHoriOrient ) + nRelPosX += (nWidth / 2) - (nObjWidth / 2); + else if ( text::HoriOrientation::RIGHT == eHoriOrient ) + nRelPosX += nWidth - + ( nObjWidth + + ( aRectFnSet.IsVert() ? _rULSpacing.GetLower() : _rLRSpacing.GetRight() ) ); + else + nRelPosX += aRectFnSet.IsVert() ? _rULSpacing.GetUpper() : _rLRSpacing.GetLeft(); + + // adjust relative position by distance between anchor frame and + // the frame, the object is oriented at. + if ( &rAnchorFrame != &_rHoriOrientFrame ) + { + SwTwips nLeftOrient = aRectFnSet.GetLeft(_rHoriOrientFrame.getFrameArea()); + SwTwips nLeftAnchor = aRectFnSet.GetLeft(rAnchorFrame.getFrameArea()); + nRelPosX += aRectFnSet.XDiff( nLeftOrient, nLeftAnchor ); + } + + // adjust calculated relative horizontal position, in order to + // keep object inside 'page' alignment layout frame + const SwFrame& rEnvironmentLayFrame = + _rEnvOfObj.GetHoriEnvironmentLayoutFrame( _rHoriOrientFrame ); + bool bFollowTextFlow = GetFrameFormat().GetFollowTextFlow().GetValue(); + bool bWrapThrough = GetFrameFormat().GetSurround().GetSurround() != text::WrapTextMode_THROUGH; + // Don't try to keep wrap-though objects inside the cell, even if they are following text flow. + if (!rEnvironmentLayFrame.IsInTab() || !bFollowTextFlow || bWrapThrough) + { + nRelPosX = AdjustHoriRelPos( rEnvironmentLayFrame, nRelPosX ); + } + + // if object is a Writer fly frame and it's anchored to a content and + // it is horizontal positioned left or right, but not relative to character, + // it has to be drawn aside another object, which have the same horizontal + // position and lay below it. + if ( GetAnchoredObj().DynCastFlyFrame() && + ( mpContact->ObjAnchoredAtPara() || mpContact->ObjAnchoredAtChar() ) && + ( eHoriOrient == text::HoriOrientation::LEFT || eHoriOrient == text::HoriOrientation::RIGHT ) && + eRelOrient != text::RelOrientation::CHAR ) + { + nRelPosX = AdjustHoriRelPosForDrawAside( _rHoriOrientFrame, + nRelPosX, _nRelPosY, + eHoriOrient, eRelOrient, + _rLRSpacing, _rULSpacing, + bEvenPage ); + } + + // #i26791# + _roHoriOffsetToFrameAnchorPos = nOffset; + + return nRelPosX; +} + +// method incl. helper methods for adjusting proposed horizontal position, +// if object has to draw aside another object. +/** adjust calculated horizontal position in order to draw object + aside other objects with same positioning +*/ +SwTwips SwAnchoredObjectPosition::AdjustHoriRelPosForDrawAside( + const SwFrame& _rHoriOrientFrame, + const SwTwips _nProposedRelPosX, + const SwTwips _nRelPosY, + const sal_Int16 _eHoriOrient, + const sal_Int16 _eRelOrient, + const SvxLRSpaceItem& _rLRSpacing, + const SvxULSpaceItem& _rULSpacing, + const bool _bEvenPage + ) const +{ + // #i26791# + if ( GetAnchorFrame().DynCastTextFrame() == nullptr || + dynamic_cast<const SwFlyAtContentFrame*>( &GetAnchoredObj() ) == nullptr ) + { + OSL_FAIL( "<SwAnchoredObjectPosition::AdjustHoriRelPosForDrawAside(..) - usage for wrong anchor type" ); + return _nProposedRelPosX; + } + + const SwTextFrame& rAnchorTextFrame = static_cast<const SwTextFrame&>(GetAnchorFrame()); + // #i26791# + const SwFlyAtContentFrame& rFlyAtContentFrame = + static_cast<const SwFlyAtContentFrame&>(GetAnchoredObj()); + const SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() ); + SwRectFnSet aRectFnSet(&_rHoriOrientFrame); + + SwTwips nAdjustedRelPosX = _nProposedRelPosX; + + // determine proposed object bound rectangle + Point aTmpPos = aRectFnSet.GetPos(rAnchorTextFrame.getFrameArea()); + if( aRectFnSet.IsVert() ) + { + aTmpPos.AdjustX( -(_nRelPosY + aObjBoundRect.Width()) ); + aTmpPos.AdjustY(nAdjustedRelPosX ); + } + else + { + aTmpPos.AdjustX(nAdjustedRelPosX ); + aTmpPos.AdjustY(_nRelPosY ); + } + SwRect aTmpObjRect( aTmpPos, aObjBoundRect.SSize() ); + + const sal_uInt32 nObjOrdNum = GetObject().GetOrdNum(); + const SwPageFrame* pObjPage = rFlyAtContentFrame.FindPageFrame(); + const SwFrame* pObjContext = ::FindContext( &rAnchorTextFrame, SwFrameType::Column ); + SwNodeOffset nObjIndex = rAnchorTextFrame.GetTextNodeFirst()->GetIndex(); + SwOrderIter aIter( pObjPage ); + const SwFlyFrame* pFly = static_cast<const SwVirtFlyDrawObj*>(aIter.Bottom())->GetFlyFrame(); + while ( pFly && nObjOrdNum > pFly->GetVirtDrawObj()->GetOrdNumDirect() ) + { + if ( DrawAsideFly( pFly, aTmpObjRect, pObjContext, nObjIndex, + _bEvenPage, _eHoriOrient, _eRelOrient ) ) + { + if( aRectFnSet.IsVert() ) + { + const SvxULSpaceItem& rOtherUL = pFly->GetFormat()->GetULSpace(); + const SwTwips nOtherTop = pFly->getFrameArea().Top() - rOtherUL.GetUpper(); + const SwTwips nOtherBot = pFly->getFrameArea().Bottom() + rOtherUL.GetLower(); + if ( nOtherTop <= aTmpObjRect.Bottom() + _rULSpacing.GetLower() && + nOtherBot >= aTmpObjRect.Top() - _rULSpacing.GetUpper() ) + { + if ( _eHoriOrient == text::HoriOrientation::LEFT ) + { + SwTwips nTmp = nOtherBot + 1 + _rULSpacing.GetUpper() - + rAnchorTextFrame.getFrameArea().Top(); + if ( nTmp > nAdjustedRelPosX && + rAnchorTextFrame.getFrameArea().Top() + nTmp + + aObjBoundRect.Height() + _rULSpacing.GetLower() + <= pObjPage->getFrameArea().Height() + pObjPage->getFrameArea().Top() ) + { + nAdjustedRelPosX = nTmp; + } + } + else if ( _eHoriOrient == text::HoriOrientation::RIGHT ) + { + SwTwips nTmp = nOtherTop - 1 - _rULSpacing.GetLower() - + aObjBoundRect.Height() - + rAnchorTextFrame.getFrameArea().Top(); + if ( nTmp < nAdjustedRelPosX && + rAnchorTextFrame.getFrameArea().Top() + nTmp - _rULSpacing.GetUpper() + >= pObjPage->getFrameArea().Top() ) + { + nAdjustedRelPosX = nTmp; + } + } + aTmpObjRect.Pos().setY( rAnchorTextFrame.getFrameArea().Top() + + nAdjustedRelPosX ); + } + } + else + { + const SvxLRSpaceItem& rOtherLR = pFly->GetFormat()->GetLRSpace(); + const SwTwips nOtherLeft = pFly->getFrameArea().Left() - rOtherLR.GetLeft(); + const SwTwips nOtherRight = pFly->getFrameArea().Right() + rOtherLR.GetRight(); + if( nOtherLeft <= aTmpObjRect.Right() + _rLRSpacing.GetRight() && + nOtherRight >= aTmpObjRect.Left() - _rLRSpacing.GetLeft() ) + { + if ( _eHoriOrient == text::HoriOrientation::LEFT ) + { + SwTwips nTmp = nOtherRight + 1 + _rLRSpacing.GetLeft() - + rAnchorTextFrame.getFrameArea().Left(); + if ( nTmp > nAdjustedRelPosX && + rAnchorTextFrame.getFrameArea().Left() + nTmp + + aObjBoundRect.Width() + _rLRSpacing.GetRight() + <= pObjPage->getFrameArea().Width() + pObjPage->getFrameArea().Left() ) + { + nAdjustedRelPosX = nTmp; + } + } + else if ( _eHoriOrient == text::HoriOrientation::RIGHT ) + { + SwTwips nTmp = nOtherLeft - 1 - _rLRSpacing.GetRight() - + aObjBoundRect.Width() - + rAnchorTextFrame.getFrameArea().Left(); + if ( nTmp < nAdjustedRelPosX && + rAnchorTextFrame.getFrameArea().Left() + nTmp - _rLRSpacing.GetLeft() + >= pObjPage->getFrameArea().Left() ) + { + nAdjustedRelPosX = nTmp; + } + } + aTmpObjRect.Pos().setX( rAnchorTextFrame.getFrameArea().Left() + + nAdjustedRelPosX ); + } + } // end of <if (bVert)> + } // end of <if DrawAsideFly(..)> + + pFly = static_cast<const SwVirtFlyDrawObj*>(aIter.Next())->GetFlyFrame(); + } // end of <loop on fly frames + + return nAdjustedRelPosX; +} + +/** determine, if object has to draw aside given fly frame + + method used by <AdjustHoriRelPosForDrawAside(..)> +*/ +bool SwAnchoredObjectPosition::DrawAsideFly( const SwFlyFrame* _pFly, + const SwRect& _rObjRect, + const SwFrame* _pObjContext, + const SwNodeOffset _nObjIndex, + const bool _bEvenPage, + const sal_Int16 _eHoriOrient, + const sal_Int16 _eRelOrient + ) const +{ + bool bRetVal = false; + + SwRectFnSet aRectFnSet(&GetAnchorFrame()); + + if ( _pFly->IsFlyAtContentFrame() && + aRectFnSet.BottomDist( _pFly->getFrameArea(), aRectFnSet.GetTop(_rObjRect) ) < 0 && + aRectFnSet.BottomDist( _rObjRect, aRectFnSet.GetTop(_pFly->getFrameArea()) ) < 0 && + ::FindContext( _pFly->GetAnchorFrame(), SwFrameType::Column ) == _pObjContext ) + { + SwNodeOffset nOtherIndex = + static_cast<const SwTextFrame*>(_pFly->GetAnchorFrame())->GetTextNodeFirst()->GetIndex(); + if (sw::FrameContainsNode(static_cast<SwTextFrame const&>(*_pFly->GetAnchorFrame()), _nObjIndex) + || nOtherIndex < _nObjIndex) + { + const SwFormatHoriOrient& rHori = _pFly->GetFormat()->GetHoriOrient(); + sal_Int16 eOtherRelOrient = rHori.GetRelationOrient(); + if( text::RelOrientation::CHAR != eOtherRelOrient ) + { + sal_Int16 eOtherHoriOrient = rHori.GetHoriOrient(); + ToggleHoriOrientAndAlign( _bEvenPage && rHori.IsPosToggle(), + eOtherHoriOrient, + eOtherRelOrient ); + if ( eOtherHoriOrient == _eHoriOrient && + Minor_( _eRelOrient, eOtherRelOrient, text::HoriOrientation::LEFT == _eHoriOrient ) ) + { + bRetVal = true; + } + } + } + } + + return bRetVal; +} + +/** determine, if object has to draw aside another object + + the different alignments of the objects determines, if one has + to draw aside another one. Thus, the given alignment are checked + against each other, which one has to be drawn aside the other one. + depending on parameter _bLeft check is done for left or right + positioning. + method used by <DrawAsideFly(..)> +*/ +bool SwAnchoredObjectPosition::Minor_( sal_Int16 _eRelOrient1, + sal_Int16 _eRelOrient2, + bool _bLeft ) +{ + bool bRetVal; + + // draw aside order for left horizontal position + //! one array entry for each value in text::RelOrientation + static sal_uInt16 const aLeft[ 10 ] = + { 5, 6, 0, 1, 8, 4, 7, 2, 3, 9 }; + // draw aside order for right horizontal position + //! one array entry for each value in text::RelOrientation + static sal_uInt16 const aRight[ 10 ] = + { 5, 6, 0, 8, 1, 7, 4, 2, 3, 9 }; + + // decide depending on given order, which frame has to draw aside another frame + if( _bLeft ) + bRetVal = aLeft[ _eRelOrient1 ] >= aLeft[ _eRelOrient2 ]; + else + bRetVal = aRight[ _eRelOrient1 ] >= aRight[ _eRelOrient2 ]; + + return bRetVal; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/objectpositioning/ascharanchoredobjectposition.cxx b/sw/source/core/objectpositioning/ascharanchoredobjectposition.cxx new file mode 100644 index 0000000000..e82659ccd5 --- /dev/null +++ b/sw/source/core/objectpositioning/ascharanchoredobjectposition.cxx @@ -0,0 +1,398 @@ +/* -*- 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 <anchoreddrawobject.hxx> +#include <ascharanchoredobjectposition.hxx> +#include <frame.hxx> +#include <txtfrm.hxx> +#include <flyfrms.hxx> +#include <svx/svdobj.hxx> +#include <frmfmt.hxx> +#include <frmatr.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <osl/diagnose.h> +#include <fmtornt.hxx> + + +using namespace ::com::sun::star; + +namespace objectpositioning +{ + +/** constructor */ +SwAsCharAnchoredObjectPosition::SwAsCharAnchoredObjectPosition( + SdrObject& _rDrawObj, + const Point& _rProposedAnchorPos, + const AsCharFlags _nFlags, + const SwTwips _nLineAscent, + const SwTwips _nLineDescent, + const SwTwips _nLineAscentInclObjs, + const SwTwips _nLineDescentInclObjs ) + : SwAnchoredObjectPosition( _rDrawObj ), + mrProposedAnchorPos( _rProposedAnchorPos ), + mnFlags( _nFlags ), + mnLineAscent( _nLineAscent ), + mnLineDescent( _nLineDescent ), + mnLineAscentInclObjs( _nLineAscentInclObjs ), + mnLineDescentInclObjs( _nLineDescentInclObjs ), + mnRelPos ( 0 ), + mnLineAlignment ( sw::LineAlign::NONE ) +{} + +/** destructor */ +SwAsCharAnchoredObjectPosition::~SwAsCharAnchoredObjectPosition() +{} + +/** method to cast <SwAnchoredObjectPosition::GetAnchorFrame()> to needed type */ +const SwTextFrame& SwAsCharAnchoredObjectPosition::GetAnchorTextFrame() const +{ + assert( dynamic_cast<const SwTextFrame*>( &GetAnchorFrame() ) && + "SwAsCharAnchoredObjectPosition::GetAnchorTextFrame() - wrong anchor frame type" ); + + return static_cast<const SwTextFrame&>(GetAnchorFrame()); +} + +/** calculate position for object + + OD 30.07.2003 #110978# + members <maAnchorPos>, <mnRelPos>, <maObjBoundRect> and + <mnLineAlignment> are calculated. + calculated position is set at the given object. +*/ +void SwAsCharAnchoredObjectPosition::CalcPosition() +{ + const SwTextFrame& rAnchorFrame = GetAnchorTextFrame(); + // swap anchor frame, if swapped. Note: destructor takes care of the 'undo' + SwFrameSwapper aFrameSwapper( &rAnchorFrame, false ); + + SwRectFnSet aRectFnSet(&rAnchorFrame); + + Point aAnchorPos( mrProposedAnchorPos ); + + const SwFrameFormat& rFrameFormat = GetFrameFormat(); + + SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() ); + SwTwips nObjWidth = aRectFnSet.GetWidth(aObjBoundRect); + + // determine spacing values considering layout-/text-direction + const SvxLRSpaceItem& rLRSpace = rFrameFormat.GetLRSpace(); + const SvxULSpaceItem& rULSpace = rFrameFormat.GetULSpace(); + SwTwips nLRSpaceLeft, nLRSpaceRight, nULSpaceUpper, nULSpaceLower; + { + if ( rAnchorFrame.IsVertical() ) + { + // Seems to be easier to do it all the horizontal way + // So, from now on think horizontal. + rAnchorFrame.SwitchVerticalToHorizontal( aObjBoundRect ); + rAnchorFrame.SwitchVerticalToHorizontal( aAnchorPos ); + + // convert the spacing values + nLRSpaceLeft = rULSpace.GetUpper(); + nLRSpaceRight = rULSpace.GetLower(); + nULSpaceUpper = rLRSpace.GetRight(); + nULSpaceLower = rLRSpace.GetLeft(); + } + else + { + if ( rAnchorFrame.IsRightToLeft() ) + { + nLRSpaceLeft = rLRSpace.GetRight(); + nLRSpaceRight = rLRSpace.GetLeft(); + } + else + { + nLRSpaceLeft = rLRSpace.GetLeft(); + nLRSpaceRight = rLRSpace.GetRight(); + } + + nULSpaceUpper = rULSpace.GetUpper(); + nULSpaceLower = rULSpace.GetLower(); + } + } + + // consider left and upper spacing by adjusting anchor position. + // left spacing is only considered, if requested. + if( mnFlags & AsCharFlags::UlSpace ) + { + aAnchorPos.AdjustX(nLRSpaceLeft ); + } + aAnchorPos.AdjustY(nULSpaceUpper ); + + // for drawing objects: consider difference between its bounding rectangle + // and its snapping rectangle by adjusting anchor position. + // left difference is only considered, if requested. + if( !IsObjFly() ) + { + SwRect aSnapRect(GetObject().GetSnapRect()); + if ( rAnchorFrame.IsVertical() ) + { + rAnchorFrame.SwitchVerticalToHorizontal( aSnapRect ); + } + + if( mnFlags & AsCharFlags::UlSpace ) + { + aAnchorPos.AdjustX(aSnapRect.Left() - aObjBoundRect.Left() ); + } + aAnchorPos.AdjustY(aSnapRect.Top() - aObjBoundRect.Top() ); + } + + // enlarge bounding rectangle of object by its spacing. + aObjBoundRect.AddLeft( - nLRSpaceLeft ); + aObjBoundRect.AddWidth( nLRSpaceRight ); + aObjBoundRect.AddTop( - nULSpaceUpper ); + aObjBoundRect.AddHeight( nULSpaceLower ); + + // calculate relative position to given base line. + const SwFormatVertOrient& rVert = rFrameFormat.GetVertOrient(); + const SwTwips nObjBoundHeight = ( mnFlags & AsCharFlags::Rotate ) + ? aObjBoundRect.Width() + : aObjBoundRect.Height(); + const SwTwips nRelPos = GetRelPosToBase( nObjBoundHeight, rVert ); + + // for initial positioning: + // adjust the proposed anchor position by difference between + // calculated relative position to base line and current maximal line ascent. + // Note: In the following line formatting the base line will be adjusted + // by the same difference. + if( mnFlags & AsCharFlags::Init && nRelPos < 0 && mnLineAscentInclObjs < -nRelPos ) + { + if( mnFlags & AsCharFlags::Rotate ) + aAnchorPos.AdjustX( -(mnLineAscentInclObjs + nRelPos) ); + else + aAnchorPos.AdjustY( -(mnLineAscentInclObjs + nRelPos) ); + } + + // consider BIDI-multiportion by adjusting proposed anchor position + if( mnFlags & AsCharFlags::Bidi ) + aAnchorPos.AdjustX( -(aObjBoundRect.Width()) ); + + // calculate relative position considering rotation and inside rotation + // reverse direction. + Point aRelPos; + { + if( mnFlags & AsCharFlags::Rotate ) + { + if( mnFlags & AsCharFlags::Reverse ) + aRelPos.setX( -nRelPos - aObjBoundRect.Width() ); + else + { + aRelPos.setX( nRelPos ); + aRelPos.setY( -aObjBoundRect.Height() ); + } + } + else + aRelPos.setY( nRelPos ); + } + + if( !IsObjFly() ) + { + if( !( mnFlags & AsCharFlags::Quick ) ) + { + // save calculated Y-position value for 'automatic' vertical positioning, + // in order to avoid a switch to 'manual' vertical positioning in + // <SwDrawContact::Changed_(..)>. + const sal_Int16 eVertOrient = rVert.GetVertOrient(); + if( rVert.GetPos() != nRelPos && eVertOrient != text::VertOrientation::NONE ) + { + SwFormatVertOrient aVert( rVert ); + aVert.SetPos( nRelPos ); + const_cast<SwFrameFormat&>(rFrameFormat).LockModify(); + const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr( aVert ); + const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify(); + } + + // determine absolute anchor position considering layout directions. + // Note: Use copy of <aAnchorPos>, because it's needed for + // setting relative position. + Point aAbsAnchorPos( aAnchorPos ); + if ( rAnchorFrame.IsRightToLeft() ) + { + rAnchorFrame.SwitchLTRtoRTL( aAbsAnchorPos ); + aAbsAnchorPos.AdjustX( -nObjWidth ); + } + if ( rAnchorFrame.IsVertical() ) + rAnchorFrame.SwitchHorizontalToVertical( aAbsAnchorPos ); + + // set proposed anchor position at the drawing object. + // OD 2004-04-06 #i26791# - distinction between 'master' drawing + // object and 'virtual' drawing object no longer needed. + GetObject().SetAnchorPos( aAbsAnchorPos ); + + // move drawing object to set its correct relative position. + { + SwRect aSnapRect(GetObject().GetSnapRect()); + if ( rAnchorFrame.IsVertical() ) + rAnchorFrame.SwitchVerticalToHorizontal( aSnapRect ); + + Point aDiff; + if ( rAnchorFrame.IsRightToLeft() ) + aDiff = aRelPos + aAbsAnchorPos - aSnapRect.TopLeft(); + else + aDiff = aRelPos + aAnchorPos - aSnapRect.TopLeft(); + + if ( rAnchorFrame.IsVertical() ) + aDiff = Point( -aDiff.Y(), aDiff.X() ); + + // OD 2004-04-06 #i26791# - distinction between 'master' drawing + // object and 'virtual' drawing object no longer needed. + GetObject().Move( Size( aDiff.X(), aDiff.Y() ) ); + } + } + + // switch horizontal, LTR anchor position to absolute values. + if ( rAnchorFrame.IsRightToLeft() ) + { + rAnchorFrame.SwitchLTRtoRTL( aAnchorPos ); + aAnchorPos.AdjustX( -nObjWidth ); + } + if ( rAnchorFrame.IsVertical() ) + rAnchorFrame.SwitchHorizontalToVertical( aAnchorPos ); + + // #i44347# - keep last object rectangle at anchored object + assert( dynamic_cast<const SwAnchoredDrawObject*>( &GetAnchoredObj() ) && + "<SwAsCharAnchoredObjectPosition::CalcPosition()> - wrong type of anchored object." ); + SwAnchoredDrawObject& rAnchoredDrawObj = + static_cast<SwAnchoredDrawObject&>( GetAnchoredObj() ); + rAnchoredDrawObj.SetLastObjRect( rAnchoredDrawObj.GetObjRect().SVRect() ); + } + else + { + // determine absolute anchor position and calculate corresponding + // relative position and its relative position attribute. + // Note: The relative position contains the spacing values. + Point aRelAttr; + if ( rAnchorFrame.IsRightToLeft() ) + { + rAnchorFrame.SwitchLTRtoRTL( aAnchorPos ); + aAnchorPos.AdjustX( -nObjWidth ); + } + if ( rAnchorFrame.IsVertical() ) + { + rAnchorFrame.SwitchHorizontalToVertical( aAnchorPos ); + aRelAttr = Point( -nRelPos, 0 ); + aRelPos = Point( -aRelPos.Y(), aRelPos.X() ); + } + else + aRelAttr = Point( 0, nRelPos ); + + // OD 2004-03-23 #i26791# + assert( dynamic_cast<const SwFlyInContentFrame*>( &GetAnchoredObj()) && + "<SwAsCharAnchoredObjectPosition::CalcPosition()> - wrong anchored object." ); + const SwFlyInContentFrame& rFlyInContentFrame = + static_cast<const SwFlyInContentFrame&>(GetAnchoredObj()); + if ( !(mnFlags & AsCharFlags::Quick) && + ( aAnchorPos != rFlyInContentFrame.GetRefPoint() || + aRelAttr != rFlyInContentFrame.GetCurrRelPos() ) ) + { + // set new anchor position and relative position + SwFlyInContentFrame* pFlyInContentFrame = &const_cast<SwFlyInContentFrame&>(rFlyInContentFrame); + pFlyInContentFrame->SetRefPoint( aAnchorPos, aRelAttr, aRelPos ); + if( nObjWidth != aRectFnSet.GetWidth(pFlyInContentFrame->getFrameArea()) ) + { + // recalculate object bound rectangle, if object width has changed. + aObjBoundRect = GetAnchoredObj().GetObjRect(); + aObjBoundRect.AddLeft( - rLRSpace.GetLeft() ); + aObjBoundRect.AddWidth( rLRSpace.GetRight() ); + aObjBoundRect.AddTop( - rULSpace.GetUpper() ); + aObjBoundRect.AddHeight( rULSpace.GetLower() ); + } + } + OSL_ENSURE( aRectFnSet.GetHeight(rFlyInContentFrame.getFrameArea()), + "SwAnchoredObjectPosition::CalcPosition(..) - fly frame has an invalid height" ); + } + + // keep calculated values + maAnchorPos = aAnchorPos; + mnRelPos = nRelPos; + maObjBoundRect = aObjBoundRect; +} + +/** determine the relative position to base line for object position type AS_CHAR + + OD 29.07.2003 #110978# + Note about values set at member <mnLineAlignment> - + value gives feedback for the line formatting. + 0 - no feedback; 1|2|3 - proposed formatting of characters + at top|at center|at bottom of line. +*/ +SwTwips SwAsCharAnchoredObjectPosition::GetRelPosToBase( + const SwTwips _nObjBoundHeight, + const SwFormatVertOrient& _rVert ) +{ + SwTwips nRelPosToBase = 0; + + mnLineAlignment = sw::LineAlign::NONE; + + const sal_Int16 eVertOrient = _rVert.GetVertOrient(); + + if ( eVertOrient == text::VertOrientation::NONE ) + nRelPosToBase = _rVert.GetPos(); + else + { + if ( eVertOrient == text::VertOrientation::CENTER ) + nRelPosToBase -= _nObjBoundHeight / 2; + else if ( eVertOrient == text::VertOrientation::TOP ) + nRelPosToBase -= _nObjBoundHeight; + else if ( eVertOrient == text::VertOrientation::BOTTOM ) + nRelPosToBase = 0; + else if ( eVertOrient == text::VertOrientation::CHAR_CENTER ) + nRelPosToBase -= ( _nObjBoundHeight + mnLineAscent - mnLineDescent ) / 2; + else if ( eVertOrient == text::VertOrientation::CHAR_TOP ) + nRelPosToBase -= mnLineAscent; + else if ( eVertOrient == text::VertOrientation::CHAR_BOTTOM ) + nRelPosToBase += mnLineDescent - _nObjBoundHeight; + else + { + if( _nObjBoundHeight >= mnLineAscentInclObjs + mnLineDescentInclObjs ) + { + // object is at least as high as the line. Thus, no more is + // positioning necessary. Also, the max. ascent isn't changed. + nRelPosToBase -= mnLineAscentInclObjs; + if ( eVertOrient == text::VertOrientation::LINE_CENTER ) + mnLineAlignment = sw::LineAlign::CENTER; + else if ( eVertOrient == text::VertOrientation::LINE_TOP ) + mnLineAlignment = sw::LineAlign::TOP; + else if ( eVertOrient == text::VertOrientation::LINE_BOTTOM ) + mnLineAlignment = sw::LineAlign::BOTTOM; + } + else if ( eVertOrient == text::VertOrientation::LINE_CENTER ) + { + nRelPosToBase -= ( _nObjBoundHeight + mnLineAscentInclObjs - mnLineDescentInclObjs ) / 2; + mnLineAlignment = sw::LineAlign::CENTER; + } + else if ( eVertOrient == text::VertOrientation::LINE_TOP ) + { + nRelPosToBase -= mnLineAscentInclObjs; + mnLineAlignment = sw::LineAlign::TOP; + } + else if ( eVertOrient == text::VertOrientation::LINE_BOTTOM ) + { + nRelPosToBase += mnLineDescentInclObjs - _nObjBoundHeight; + mnLineAlignment = sw::LineAlign::BOTTOM; + } + } + } + + return nRelPosToBase; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/objectpositioning/environmentofanchoredobject.cxx b/sw/source/core/objectpositioning/environmentofanchoredobject.cxx new file mode 100644 index 0000000000..e6529be386 --- /dev/null +++ b/sw/source/core/objectpositioning/environmentofanchoredobject.cxx @@ -0,0 +1,99 @@ +/* -*- 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 <environmentofanchoredobject.hxx> +#include <frame.hxx> +#include <pagefrm.hxx> +#include <osl/diagnose.h> + +namespace objectpositioning +{ + +SwEnvironmentOfAnchoredObject::SwEnvironmentOfAnchoredObject( + const bool _bFollowTextFlow ) + : mbFollowTextFlow( _bFollowTextFlow ) +{} + +/** determine environment layout frame for possible horizontal object positions */ +const SwLayoutFrame& SwEnvironmentOfAnchoredObject::GetHoriEnvironmentLayoutFrame( + const SwFrame& _rHoriOrientFrame ) const +{ + const SwFrame* pHoriEnvironmentLayFrame = &_rHoriOrientFrame; + + if ( !mbFollowTextFlow ) + { + // No exception any more for page alignment. + // the page frame determines the horizontal layout environment. + pHoriEnvironmentLayFrame = _rHoriOrientFrame.FindPageFrame(); + } + else + { + while ( !pHoriEnvironmentLayFrame->IsCellFrame() && + !pHoriEnvironmentLayFrame->IsFlyFrame() && + !pHoriEnvironmentLayFrame->IsPageFrame() ) + { + pHoriEnvironmentLayFrame = pHoriEnvironmentLayFrame->GetUpper(); + OSL_ENSURE( pHoriEnvironmentLayFrame, + "SwEnvironmentOfAnchoredObject::GetHoriEnvironmentLayoutFrame(..) - no page|fly|cell frame found" ); + } + } + + assert( dynamic_cast< const SwLayoutFrame *>( pHoriEnvironmentLayFrame ) && + "SwEnvironmentOfAnchoredObject::GetHoriEnvironmentLayoutFrame(..) - found frame isn't a layout frame" ); + + return static_cast<const SwLayoutFrame&>(*pHoriEnvironmentLayFrame); +} + +/** determine environment layout frame for possible vertical object positions */ +const SwLayoutFrame& SwEnvironmentOfAnchoredObject::GetVertEnvironmentLayoutFrame( + const SwFrame& _rVertOrientFrame ) const +{ + const SwFrame* pVertEnvironmentLayFrame = &_rVertOrientFrame; + + if ( !mbFollowTextFlow ) + { + // No exception any more for page alignment. + // the page frame determines the vertical layout environment. + pVertEnvironmentLayFrame = _rVertOrientFrame.FindPageFrame(); + } + else + { + while ( !pVertEnvironmentLayFrame->IsCellFrame() && + !pVertEnvironmentLayFrame->IsFlyFrame() && + !pVertEnvironmentLayFrame->IsHeaderFrame() && + !pVertEnvironmentLayFrame->IsFooterFrame() && + !pVertEnvironmentLayFrame->IsFootnoteFrame() && + !pVertEnvironmentLayFrame->IsPageBodyFrame() && + !pVertEnvironmentLayFrame->IsPageFrame() ) + { + pVertEnvironmentLayFrame = pVertEnvironmentLayFrame->GetUpper(); + OSL_ENSURE( pVertEnvironmentLayFrame, + "SwEnvironmentOfAnchoredObject::GetVertEnvironmentLayoutFrame(..) - proposed frame not found" ); + } + } + + assert( dynamic_cast< const SwLayoutFrame *>( pVertEnvironmentLayFrame ) && + "SwEnvironmentOfAnchoredObject::GetVertEnvironmentLayoutFrame(..) - found frame isn't a layout frame" ); + + return static_cast<const SwLayoutFrame&>(*pVertEnvironmentLayFrame); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx b/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx new file mode 100644 index 0000000000..8e4af98b71 --- /dev/null +++ b/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx @@ -0,0 +1,1335 @@ +/* -*- 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 <tocntntanchoredobjectposition.hxx> +#include <anchoredobject.hxx> +#include <frame.hxx> +#include <txtfrm.hxx> +#include <pagefrm.hxx> +#include <sectfrm.hxx> +#include <tabfrm.hxx> +#include <rootfrm.hxx> +#include <viewopt.hxx> +#include <viewsh.hxx> +#include <frmfmt.hxx> +#include <fmtsrnd.hxx> +#include <fmtfsize.hxx> +#include <fmtanchr.hxx> +#include <fmtornt.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <svx/svdobj.hxx> +#include <osl/diagnose.h> +#include <environmentofanchoredobject.hxx> +#include <frmatr.hxx> +#include <fmtwrapinfluenceonobjpos.hxx> +#include <sortedobjs.hxx> +#include <textboxhelper.hxx> +#include <flyfrms.hxx> + +using namespace ::com::sun::star; + +namespace objectpositioning +{ +SwToContentAnchoredObjectPosition::SwToContentAnchoredObjectPosition( SdrObject& _rDrawObj ) + : SwAnchoredObjectPosition ( _rDrawObj ), + mpVertPosOrientFrame( nullptr ), + mbAnchorToChar ( false ), + mpToCharOrientFrame( nullptr ), + mpToCharRect( nullptr ), + // #i22341# + mnToCharTopOfLine( 0 ) +{} + +SwToContentAnchoredObjectPosition::~SwToContentAnchoredObjectPosition() +{} + +bool SwToContentAnchoredObjectPosition::IsAnchoredToChar() const +{ + return mbAnchorToChar; +} + +const SwFrame* SwToContentAnchoredObjectPosition::ToCharOrientFrame() const +{ + return mpToCharOrientFrame; +} + +const SwRect* SwToContentAnchoredObjectPosition::ToCharRect() const +{ + return mpToCharRect; +} + +// #i22341# +SwTwips SwToContentAnchoredObjectPosition::ToCharTopOfLine() const +{ + return mnToCharTopOfLine; +} + +SwTextFrame& SwToContentAnchoredObjectPosition::GetAnchorTextFrame() const +{ + assert( dynamic_cast<const SwTextFrame*>( &GetAnchorFrame()) && + "SwToContentAnchoredObjectPosition::GetAnchorTextFrame() - wrong anchor frame type" ); + + return static_cast<SwTextFrame&>(GetAnchorFrame()); +} + +// #i23512# +static bool lcl_DoesVertPosFits( const SwTwips _nRelPosY, + const SwTwips _nAvail, + const SwLayoutFrame* _pUpperOfOrientFrame, + const bool _bBrowse, + const bool _bGrowInTable, + SwLayoutFrame*& _orpLayoutFrameToGrow ) +{ + bool bVertPosFits = false; + + if ( _nRelPosY <= _nAvail ) + { + bVertPosFits = true; + } + else if ( _bBrowse ) + { + if ( _pUpperOfOrientFrame->IsInSct() ) + { + SwSectionFrame* pSctFrame = + const_cast<SwSectionFrame*>(_pUpperOfOrientFrame->FindSctFrame()); + bVertPosFits = pSctFrame->GetUpper()->Grow( _nRelPosY - _nAvail, true ) > 0; + // Note: do not provide a layout frame for a grow. + } + else + { + bVertPosFits = const_cast<SwLayoutFrame*>(_pUpperOfOrientFrame)-> + Grow( _nRelPosY - _nAvail, true ) > 0; + if ( bVertPosFits ) + _orpLayoutFrameToGrow = const_cast<SwLayoutFrame*>(_pUpperOfOrientFrame); + } + } + else if ( _pUpperOfOrientFrame->IsInTab() && _bGrowInTable ) + { + // #i45085# - check, if upper frame would grow the + // expected amount of twips. + const SwTwips nTwipsGrown = const_cast<SwLayoutFrame*>(_pUpperOfOrientFrame)-> + Grow( _nRelPosY - _nAvail, true ); + bVertPosFits = ( nTwipsGrown == ( _nRelPosY - _nAvail ) ); + if ( bVertPosFits ) + _orpLayoutFrameToGrow = const_cast<SwLayoutFrame*>(_pUpperOfOrientFrame); + } + + return bVertPosFits; +} + +void SwToContentAnchoredObjectPosition::CalcPosition() +{ + // get format of object + const SwFrameFormat& rFrameFormat = GetFrameFormat(); + + // declare and set <pFooter> to footer frame, if object is anchored + // at a frame belonging to the footer. + const SwFrame* pFooter = GetAnchorFrame().FindFooterOrHeader(); + if ( pFooter && !pFooter->IsFooterFrame() ) + pFooter = nullptr; + + // declare and set <bBrowse> to true, if document is in browser mode and + // object is anchored at the body, but not at frame belonging to a table. + bool bBrowse = GetAnchorFrame().IsInDocBody() && !GetAnchorFrame().IsInTab(); + if( bBrowse ) + { + const SwViewShell *pSh = GetAnchorFrame().getRootFrame()->GetCurrShell(); + if( !pSh || !pSh->GetViewOptions()->getBrowseMode() ) + bBrowse = false; + } + + // determine left/right and its upper/lower spacing. + const SvxLRSpaceItem &rLR = rFrameFormat.GetLRSpace(); + const SvxULSpaceItem &rUL = rFrameFormat.GetULSpace(); + + // determine, if object has no surrounding. + const SwFormatSurround& rSurround = rFrameFormat.GetSurround(); + const bool bNoSurround = rSurround.GetSurround() == css::text::WrapTextMode_NONE; + const bool bWrapThrough = rSurround.GetSurround() == css::text::WrapTextMode_THROUGH; + + // new class <SwEnvironmentOfAnchoredObject> + SwEnvironmentOfAnchoredObject aEnvOfObj( DoesObjFollowsTextFlow() ); + + // #i18732# - grow only, if object has to follow the text flow + const bool bGrow = DoesObjFollowsTextFlow() && + ( !GetAnchorFrame().IsInTab() || + !rFrameFormat.GetFrameSize().GetHeightPercent() ); + + // get text frame the object is anchored at + const SwTextFrame& rAnchorTextFrame = GetAnchorTextFrame(); + SwRectFnSet aRectFnSet(&rAnchorTextFrame); + + const SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() ); + + // local variable keeping the calculated relative position; initialized with + // current relative position. + // #i26791# - use new object instance of <SwAnchoredObject> + Point aRelPos( GetAnchoredObj().GetCurrRelPos() ); + + SwTwips nRelDiff = 0; + + bool bMoveable = rAnchorTextFrame.IsMoveable(); + + // determine frame the object position has to be oriented at. + const SwTextFrame* pOrientFrame = &rAnchorTextFrame; + const SwTextFrame* pAnchorFrameForVertPos; + // If true, this means that the anchored object is a split fly frame and it's not a master but + // one of the follows. + bool bFollowSplitFly = false; + // The anchored object is a fly that is allowed to split. + bool bSplitFly = false; + { + // if object is at-character anchored, determine character-rectangle + // and frame, position has to be oriented at. + mbAnchorToChar = (RndStdIds::FLY_AT_CHAR == rFrameFormat.GetAnchor().GetAnchorId()); + if ( mbAnchorToChar ) + { + const SwFormatAnchor& rAnch = rFrameFormat.GetAnchor(); + // #i26791# - use new object instance of <SwAnchoredObject> + // Due to table break algorithm the character + // rectangle can have no height. Thus, check also the width + if ( ( !GetAnchoredObj().GetLastCharRect().Height() && + !GetAnchoredObj().GetLastCharRect().Width() ) || + !GetAnchoredObj().GetLastTopOfLine() ) + { + GetAnchoredObj().CheckCharRectAndTopOfLine( false ); + // Due to table break algorithm the character + // rectangle can have no height. Thus, check also the width + if ( ( !GetAnchoredObj().GetLastCharRect().Height() && + !GetAnchoredObj().GetLastCharRect().Width() ) || + !GetAnchoredObj().GetLastTopOfLine() ) + { + // Get default for <mpVertPosOrientFrame>, if it's not set. + if ( !mpVertPosOrientFrame ) + { + mpVertPosOrientFrame = rAnchorTextFrame.GetUpper(); + } + return; + } + } + mpToCharRect = &(GetAnchoredObj().GetLastCharRect()); + // #i22341# - get top of line, in which the anchor character is. + mnToCharTopOfLine = GetAnchoredObj().GetLastTopOfLine(); + pOrientFrame = &(const_cast<SwTextFrame&>(rAnchorTextFrame).GetFrameAtOfst( + rAnchorTextFrame.MapModelToViewPos(*rAnch.GetContentAnchor()))); + mpToCharOrientFrame = pOrientFrame; + } + else if (SwFlyFrame* pFlyFrame = GetAnchoredObj().DynCastFlyFrame()) + { + // See if this fly is split. If so, then the anchor is also split. All anchors are + // empty, except the last follow. + if (pFlyFrame->IsFlySplitAllowed()) + { + auto pFlyAtContentFrame = static_cast<SwFlyAtContentFrame*>(pFlyFrame); + // Decrement pFly to point to the master; increment pAnchor to point to the correct + // follow anchor. + SwFlyAtContentFrame* pFly = pFlyAtContentFrame; + SwTextFrame* pAnchor = const_cast<SwTextFrame*>(&rAnchorTextFrame); + while (pFly->GetPrecede()) + { + pFly = pFly->GetPrecede(); + if (!pAnchor) + { + SAL_WARN("sw.core", "SwToContentAnchoredObjectPosition::CalcPosition: fly " + "chain length is longer then anchor chain length"); + break; + } + pAnchor = pAnchor->GetFollow(); + } + if (pAnchor && pAnchor->GetPrecede()) + { + pOrientFrame = pAnchor; + // Anchored object has a precede, so it's a follow. + bFollowSplitFly = true; + } + bSplitFly = true; + } + } + } + aRectFnSet.Refresh(pOrientFrame); + + // determine vertical position + { + + // determine vertical positioning and alignment attributes + SwFormatVertOrient aVert( rFrameFormat.GetVertOrient() ); + + // #i18732# - determine layout frame for vertical + // positions aligned to 'page areas'. + const SwLayoutFrame& rPageAlignLayFrame = + aEnvOfObj.GetVertEnvironmentLayoutFrame( *pOrientFrame ); + + if ( aVert.GetVertOrient() != text::VertOrientation::NONE ) + { + // #i18732# - adjustments for follow text flow or not + // AND vertical alignment at 'page areas'. + SwTwips nAlignAreaHeight; + SwTwips nAlignAreaOffset; + GetVertAlignmentValues( *pOrientFrame, rPageAlignLayFrame, + aVert.GetRelationOrient(), + nAlignAreaHeight, nAlignAreaOffset ); + + SwRect aHeaderRect; + const SwPageFrame* aPageFrame = pOrientFrame->FindPageFrame(); + const SwHeaderFrame* pHeaderFrame = aPageFrame->GetHeaderFrame(); + if (pHeaderFrame) + aHeaderRect = pHeaderFrame->GetPaintArea(); + const SwTwips nTopMarginHeight = aPageFrame->GetTopMargin() + aHeaderRect.Height(); + const SwTwips nHeightBetweenOffsetAndMargin = nAlignAreaOffset + nTopMarginHeight; + + // determine relative vertical position + SwTwips nRelPosY = nAlignAreaOffset; + const SwTwips nObjHeight = aRectFnSet.GetHeight(aObjBoundRect); + const SwTwips nUpperSpace = aRectFnSet.IsVert() + ? ( aRectFnSet.IsVertL2R() + ? rLR.GetLeft() + : rLR.GetRight() ) + : rUL.GetUpper(); + // --> OD 2009-08-31 #monglianlayout# + const SwTwips nLowerSpace = aRectFnSet.IsVert() + ? ( aRectFnSet.IsVertL2R() + ? rLR.GetLeft() + : rLR.GetRight() ) + : rUL.GetLower(); + switch ( aVert.GetVertOrient() ) + { + case text::VertOrientation::CHAR_BOTTOM: + { + if ( mbAnchorToChar ) + { + // bottom (to character anchored) + nRelPosY += nAlignAreaHeight + nUpperSpace; + if ( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() ) + { + nRelPosY += nObjHeight; + } + break; + } + [[fallthrough]]; + } + case text::VertOrientation::TOP: + { + // #i22341# - special case for vertical + // alignment at top of line + if ( mbAnchorToChar && + aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE ) + { + nRelPosY -= (nObjHeight + nLowerSpace); + } + else + { + nRelPosY += nUpperSpace; + } + } + break; + // #i22341# + case text::VertOrientation::LINE_TOP: + { + if ( mbAnchorToChar && + aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE ) + { + nRelPosY -= (nObjHeight + nLowerSpace); + } + else + { + OSL_FAIL( "<SwToContentAnchoredObjectPosition::CalcPosition()> - unknown combination of vertical position and vertical alignment." ); + } + } + break; + case text::VertOrientation::CENTER: + { + if (aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA_TOP) + nRelPosY = (nAlignAreaOffset / 2) - (nObjHeight / 2) + (nHeightBetweenOffsetAndMargin / 2); + else + nRelPosY += (nAlignAreaHeight / 2) - (nObjHeight / 2); + } + break; + // #i22341# + case text::VertOrientation::LINE_CENTER: + { + if ( mbAnchorToChar && + aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE ) + { + nRelPosY += (nAlignAreaHeight / 2) - (nObjHeight / 2); + } + else + { + OSL_FAIL( "<SwToContentAnchoredObjectPosition::CalcPosition()> - unknown combination of vertical position and vertical alignment." ); + } + } + break; + case text::VertOrientation::BOTTOM: + { + if ( ( aVert.GetRelationOrient() == text::RelOrientation::FRAME || + aVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ) && + bNoSurround ) + { + // bottom (aligned to 'paragraph areas') + nRelPosY += nAlignAreaHeight + nUpperSpace; + } + else + { + // #i22341# - special case for vertical + // alignment at top of line + if ( mbAnchorToChar && + aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE ) + { + nRelPosY += nUpperSpace; + } + else + { + if (aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA_TOP) + nRelPosY = 0 - (nObjHeight + nLowerSpace) + nHeightBetweenOffsetAndMargin; + else + nRelPosY += nAlignAreaHeight - (nObjHeight + nLowerSpace); + } + } + } + break; + // #i22341# + case text::VertOrientation::LINE_BOTTOM: + { + if ( mbAnchorToChar && + aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE ) + { + nRelPosY += nUpperSpace; + } + else + { + OSL_FAIL( "<SwToContentAnchoredObjectPosition::CalcPosition()> - unknown combination of vertical position and vertical alignment." ); + } + } + break; + default: + break; + } + + // adjust relative position by distance between anchor frame and + // the frame, the object is oriented at. + // #i28701# - correction: adjust relative position, + // only if the floating screen object has to follow the text flow. + // Also don't do this for split flys: pOrientFrame already points to the follow anchor, + // so pOrientFrame is not the anchor text frame anymore, and that would lead to an + // additional, unwanted increase of nRelPosY. + if (DoesObjFollowsTextFlow() && pOrientFrame != &rAnchorTextFrame && !bFollowSplitFly) + { + // #i11860# - use new method <GetTopForObjPos> + // to get top of frame for object positioning. + const SwTwips nTopOfOrient = GetTopForObjPos( *pOrientFrame, aRectFnSet.FnRect(), aRectFnSet.IsVert() ); + nRelPosY += aRectFnSet.YDiff( nTopOfOrient, + GetTopForObjPos( rAnchorTextFrame, aRectFnSet.FnRect(), aRectFnSet.IsVert() ) ); + } + + // #i42124# - capture object inside vertical + // layout environment. + { + const SwTwips nTopOfAnch = + GetTopForObjPos( *pOrientFrame, aRectFnSet.FnRect(), aRectFnSet.IsVert() ); + const SwLayoutFrame& rVertEnvironLayFrame = + aEnvOfObj.GetVertEnvironmentLayoutFrame( + *(pOrientFrame->GetUpper()) ); + const bool bCheckBottom = !DoesObjFollowsTextFlow(); + nRelPosY = AdjustVertRelPos( nTopOfAnch, aRectFnSet.IsVert(), aRectFnSet.IsVertL2R(), + rVertEnvironLayFrame, nRelPosY, + DoesObjFollowsTextFlow(), + bCheckBottom ); + } + + // keep calculated relative vertical position - needed for filters + // (including the xml-filter) + { + // determine position + SwTwips nAttrRelPosY = nRelPosY - nAlignAreaOffset; + // set + if ( nAttrRelPosY != aVert.GetPos() ) + { + aVert.SetPos( nAttrRelPosY ); + const_cast<SwFrameFormat&>(rFrameFormat).LockModify(); + const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr( aVert ); + const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify(); + } + } + + // determine absolute 'vertical' position, depending on layout-direction + // #i26791# - determine offset to 'vertical' frame + // anchor position, depending on layout-direction + if ( aRectFnSet.IsVert() ) + { + aRelPos.setX( nRelPosY ); + maOffsetToFrameAnchorPos.setX( nAlignAreaOffset ); + } + else + { + aRelPos.setY( nRelPosY ); + maOffsetToFrameAnchorPos.setY( nAlignAreaOffset ); + } + } + + // Determine upper of frame vertical position is oriented at. + // #i28701# - determine 'virtual' anchor frame. + // This frame is used in the following instead of the 'real' anchor + // frame <rAnchorTextFrame> for the 'vertical' position in all cases. + const SwLayoutFrame* pUpperOfOrientFrame = nullptr; + { + // #i28701# - As long as the anchor frame is on the + // same page as <pOrientFrame> and the vertical position isn't aligned + // automatic at the anchor character or the top of the line of the + // anchor character, the anchor frame determines the vertical position. + // Split fly follows: always let the anchor char frame determine the vertical position. + // This gives us a vertical cut position between the master and the follow. + if ( &rAnchorTextFrame == pOrientFrame || + ( rAnchorTextFrame.FindPageFrame() == pOrientFrame->FindPageFrame() && + aVert.GetVertOrient() == text::VertOrientation::NONE && + aVert.GetRelationOrient() != text::RelOrientation::CHAR && + aVert.GetRelationOrient() != text::RelOrientation::TEXT_LINE && !bFollowSplitFly ) ) + { + pUpperOfOrientFrame = rAnchorTextFrame.GetUpper(); + pAnchorFrameForVertPos = &rAnchorTextFrame; + } + else + { + pUpperOfOrientFrame = pOrientFrame->GetUpper(); + pAnchorFrameForVertPos = pOrientFrame; + } + } + + // ignore one-column sections. + // #i23512# - correction: also ignore one-columned + // sections with footnotes/endnotes + if ( pUpperOfOrientFrame->IsInSct() ) + { + const SwSectionFrame* pSctFrame = pUpperOfOrientFrame->FindSctFrame(); + const bool bIgnoreSection = pUpperOfOrientFrame->IsSctFrame() || + ( pSctFrame->Lower()->IsColumnFrame() && + !pSctFrame->Lower()->GetNext() ); + if ( bIgnoreSection ) + pUpperOfOrientFrame = pSctFrame->GetUpper(); + } + + if ( aVert.GetVertOrient() == text::VertOrientation::NONE ) + { + // local variable <nRelPosY> for calculation of relative vertical + // distance to anchor. + SwTwips nRelPosY = 0; + // #i26791# - local variable <nVertOffsetToFrameAnchorPos> + // for determination of the 'vertical' offset to the frame anchor + // position + SwTwips nVertOffsetToFrameAnchorPos( 0 ); + // #i22341# - add special case for vertical alignment + // at top of line. + if ( mbAnchorToChar && + ( aVert.GetRelationOrient() == text::RelOrientation::CHAR || + aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE ) ) + { + // #i11860# - use new method <GetTopForObjPos> + // to get top of frame for object positioning. + SwTwips nTopOfOrient = GetTopForObjPos( *pOrientFrame, aRectFnSet.FnRect(), aRectFnSet.IsVert() ); + if ( aVert.GetRelationOrient() == text::RelOrientation::CHAR ) + { + nVertOffsetToFrameAnchorPos = aRectFnSet.YDiff( + aRectFnSet.GetBottom(*ToCharRect()), + nTopOfOrient ); + } + else + { + nVertOffsetToFrameAnchorPos = aRectFnSet.YDiff( ToCharTopOfLine(), + nTopOfOrient ); + } + nRelPosY = nVertOffsetToFrameAnchorPos - aVert.GetPos(); + } + else + { + // #i28701# - correction: use <pAnchorFrameForVertPos> + // instead of <pOrientFrame> and do not adjust relative position + // to get correct vertical position. + nVertOffsetToFrameAnchorPos = 0; + // #i11860# - use new method <GetTopForObjPos> + // to get top of frame for object positioning. + const SwTwips nTopOfOrient = + GetTopForObjPos( *pAnchorFrameForVertPos, aRectFnSet.FnRect(), aRectFnSet.IsVert() ); + // Increase <nRelPosY> by margin height, + // if position is vertical aligned to "paragraph text area" + if ( aVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ) + { + // #i11860# - consider upper space amount of previous frame + SwTwips nTopMargin = aRectFnSet.GetTopMargin(*pAnchorFrameForVertPos); + if ( pAnchorFrameForVertPos->IsTextFrame() ) + { + nTopMargin -= pAnchorFrameForVertPos-> + GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid(); + } + nVertOffsetToFrameAnchorPos += nTopMargin; + } + // #i18732# - adjust <nRelPosY> by difference + // between 'page area' and 'anchor' frame, if position is + // vertical aligned to 'page areas' + else if (aVert.GetRelationOrient() == text::RelOrientation::PAGE_FRAME + || aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA_TOP) + { + nVertOffsetToFrameAnchorPos += aRectFnSet.YDiff( + aRectFnSet.GetTop(rPageAlignLayFrame.getFrameArea()), + nTopOfOrient ); + } + else if ( aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA ) + { + SwRect aPgPrtRect( rPageAlignLayFrame.getFrameArea() ); + if ( rPageAlignLayFrame.IsPageFrame() ) + { + aPgPrtRect = + static_cast<const SwPageFrame&>(rPageAlignLayFrame).PrtWithoutHeaderAndFooter(); + } + nVertOffsetToFrameAnchorPos += aRectFnSet.YDiff( + aRectFnSet.GetTop(aPgPrtRect), + nTopOfOrient ); + } + else if (aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM) + { + // The anchored object is relative from the bottom of the page's print area. + SwRect aPgPrtRect(rPageAlignLayFrame.getFrameArea()); + if (rPageAlignLayFrame.IsPageFrame()) + { + auto& rPageFrame = static_cast<const SwPageFrame&>(rPageAlignLayFrame); + aPgPrtRect = rPageFrame.PrtWithoutHeaderAndFooter(); + } + SwTwips nPageBottom = aRectFnSet.GetBottom(aPgPrtRect); + nVertOffsetToFrameAnchorPos += aRectFnSet.YDiff(nPageBottom, nTopOfOrient); + } + nRelPosY = nVertOffsetToFrameAnchorPos + aVert.GetPos(); + if (bFollowSplitFly) + { + // This is a follow of a split fly: shift it up to match the anchor position, + // because the vertical offset is meant to be handled only on the first page. + nRelPosY -= aVert.GetPos(); + + if (aVert.GetRelationOrient() == text::RelOrientation::PAGE_FRAME + && rPageAlignLayFrame.IsPageFrame()) + { + // Master is positioned relative to the edge of the page, with an offset. + // Follow will have no offset, but is relative to the bottom of the header. + auto& rPageFrame = static_cast<const SwPageFrame&>(rPageAlignLayFrame); + const SwLayoutFrame* pBodyFrame = rPageFrame.FindBodyCont(); + if (pBodyFrame) + { + SwTwips nDiff = pBodyFrame->getFrameArea().Top() + - rPageFrame.getFrameArea().Top(); + nRelPosY += nDiff; + } + } + } + } + + // <pUpperOfOrientFrame>: layout frame, at which the position has to + // is oriented at + // <nRelPosY>: rest of the relative distance in the current + // layout frame + // <nAvail>: space, which is available in the current + // layout frame + + // #i26791# - determine offset to 'vertical' + // frame anchor position, depending on layout-direction + if ( aRectFnSet.IsVert() ) + maOffsetToFrameAnchorPos.setX( nVertOffsetToFrameAnchorPos ); + else + maOffsetToFrameAnchorPos.setY( nVertOffsetToFrameAnchorPos ); + // #i11860# - use new method <GetTopForObjPos> + // to get top of frame for object positioning. + const SwTwips nTopOfAnch = GetTopForObjPos( *pAnchorFrameForVertPos, aRectFnSet.FnRect(), aRectFnSet.IsVert() ); + if( nRelPosY <= 0 ) + { + // Allow negative position, but keep it + // inside environment layout frame. + const SwLayoutFrame& rVertEnvironLayFrame = + aEnvOfObj.GetVertEnvironmentLayoutFrame( *pUpperOfOrientFrame ); + // #i31805# - do not check, if bottom of + // anchored object would fit into environment layout frame, if + // anchored object has to follow the text flow. + const bool bCheckBottom = !DoesObjFollowsTextFlow(); + nRelPosY = AdjustVertRelPos( nTopOfAnch, aRectFnSet.IsVert(), aRectFnSet.IsVertL2R(), + rVertEnvironLayFrame, nRelPosY, + DoesObjFollowsTextFlow(), + bCheckBottom ); + if ( aRectFnSet.IsVert() ) + aRelPos.setX( nRelPosY ); + else + aRelPos.setY( nRelPosY ); + } + else + { + aRectFnSet.Refresh(pAnchorFrameForVertPos); + SwTwips nAvail = + aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame), + nTopOfAnch ); + const bool bInFootnote = pAnchorFrameForVertPos->IsInFootnote(); + while ( nRelPosY ) + { + // #i23512# - correction: + // consider section frame for grow in online layout. + // use new local method <lcl_DoesVertPosFits(..)> + SwLayoutFrame* pLayoutFrameToGrow = nullptr; + const bool bDoesVertPosFits = lcl_DoesVertPosFits( + nRelPosY, nAvail, pUpperOfOrientFrame, bBrowse, + bGrow, pLayoutFrameToGrow ); + + if ( bDoesVertPosFits ) + { + SwTwips nTmpRelPosY = + aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame), + nTopOfAnch ) - + nAvail + nRelPosY; + // #i28701# - adjust calculated + // relative vertical position to object's environment. + const SwFrame& rVertEnvironLayFrame = + aEnvOfObj.GetVertEnvironmentLayoutFrame( *pUpperOfOrientFrame ); + // Do not check, if bottom of + // anchored object would fit into environment layout + // frame, if anchored object has to follow the text flow. + const bool bCheckBottom = !DoesObjFollowsTextFlow(); + nTmpRelPosY = AdjustVertRelPos( nTopOfAnch, aRectFnSet.IsVert(), aRectFnSet.IsVertL2R(), + rVertEnvironLayFrame, + nTmpRelPosY, + DoesObjFollowsTextFlow(), + bCheckBottom ); + if ( aRectFnSet.IsVert() ) + aRelPos.setX( nTmpRelPosY ); + else + aRelPos.setY( nTmpRelPosY ); + + // #i23512# - use local variable + // <pLayoutFrameToGrow> provided by new method + // <lcl_DoesVertPosFits(..)>. + if ( pLayoutFrameToGrow ) + { + // No need to grow the anchor cell in case the follow-text-flow object + // is wrap-though. + if (!GetAnchorFrame().IsInTab() || !DoesObjFollowsTextFlow() || !bWrapThrough) + { + pLayoutFrameToGrow->Grow( nRelPosY - nAvail ); + } + } + nRelPosY = 0; + } + else + { + // #i26495# - floating screen objects, + // which are anchored inside a table, doesn't follow + // the text flow. + if ( DoesObjFollowsTextFlow() && + ( aVert.GetRelationOrient() != text::RelOrientation::PAGE_FRAME && + aVert.GetRelationOrient() != text::RelOrientation::PAGE_PRINT_AREA ) && + !GetAnchorFrame().IsInTab() ) + { + if ( bMoveable ) + { + // follow the text flow + nRelPosY -= nAvail; + MakePageType eMakePage = bInFootnote ? MAKEPAGE_NONE + : MAKEPAGE_APPEND; + const bool bInSct = pUpperOfOrientFrame->IsInSct(); + if( bInSct ) + eMakePage = MAKEPAGE_NOSECTION; + + const SwLayoutFrame* pTmp = + pUpperOfOrientFrame->GetLeaf( eMakePage, true, &rAnchorTextFrame ); + if ( pTmp && + ( !bInSct || + pUpperOfOrientFrame->FindSctFrame()->IsAnFollow( pTmp->FindSctFrame() ) ) ) + { + pUpperOfOrientFrame = pTmp; + bMoveable = rAnchorTextFrame.IsMoveable( pUpperOfOrientFrame ); + aRectFnSet.Refresh(pUpperOfOrientFrame); + nAvail = aRectFnSet.GetHeight(pUpperOfOrientFrame->getFramePrintArea()); + } + else + { + // if there isn't enough space in the (columned) + // section, leave it and set available space <nAvail> + // to the space below the section. + // if the new available space isn't also enough, + // new pages can be created. + if( bInSct ) + { + const SwFrame* pSct = pUpperOfOrientFrame->FindSctFrame(); + pUpperOfOrientFrame = pSct->GetUpper(); + nAvail = aRectFnSet.YDiff( + aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame), + aRectFnSet.GetPrtBottom(*pSct) ); + } + else + { +#if OSL_DEBUG_LEVEL > 1 + OSL_FAIL( "<SwToContentAnchoredObjectPosition::CalcPosition()> - !bInSct" ); +#endif + nRelDiff = nRelPosY; + nRelPosY = 0; + } + } + } + else + { + nRelPosY = 0; + } + } + else + { + // #i18732# - do not follow text flow respectively + // align at 'page areas', but stay inside given environment + const SwFrame& rVertEnvironLayFrame = + aEnvOfObj.GetVertEnvironmentLayoutFrame( *pUpperOfOrientFrame ); + nRelPosY = AdjustVertRelPos( nTopOfAnch, aRectFnSet.IsVert(), aRectFnSet.IsVertL2R(), + rVertEnvironLayFrame, + nRelPosY, + DoesObjFollowsTextFlow() ); + if( aRectFnSet.IsVert() ) + aRelPos.setX( nRelPosY ); + else + aRelPos.setY( nRelPosY ); + nRelPosY = 0; + } + } + } // end of <while ( nRelPosY )> + } // end of else <nRelPosY <= 0> + } // end of <aVert.GetVertOrient() == text::VertOrientation::NONE> + + // We need to calculate the part's absolute position, in order for + // it to be put onto the right page and to be pulled into the + // LayLeaf's PrtArea + const SwTwips nTopOfAnch = GetTopForObjPos( *pAnchorFrameForVertPos, aRectFnSet.FnRect(), aRectFnSet.IsVert() ); + if( aRectFnSet.IsVert() ) + { + // --> OD 2009-08-31 #monglianlayout# + if ( !aRectFnSet.IsVertL2R() ) + { + GetAnchoredObj().SetObjLeft( nTopOfAnch - + ( aRelPos.X() - nRelDiff ) - + aObjBoundRect.Width() ); + } + else + { + GetAnchoredObj().SetObjLeft( nTopOfAnch + + ( aRelPos.X() - nRelDiff ) ); + } + } + else + { + GetAnchoredObj().SetObjTop( nTopOfAnch + + ( aRelPos.Y() - nRelDiff ) ); + } + + // grow environment under certain conditions + // ignore one-column sections. + // #i23512# - correction: also ignore one-columned + // sections with footnotes/endnotes + if ( pUpperOfOrientFrame->IsInSct() ) + { + const SwSectionFrame* pSctFrame = pUpperOfOrientFrame->FindSctFrame(); + const bool bIgnoreSection = pUpperOfOrientFrame->IsSctFrame() || + ( pSctFrame->Lower()->IsColumnFrame() && + !pSctFrame->Lower()->GetNext() ); + if ( bIgnoreSection ) + pUpperOfOrientFrame = pSctFrame->GetUpper(); + } + SwTwips nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(), + aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame) ); + if( nDist < 0 ) + { + // #i23512# - correction: + // consider section frame for grow in online layout and + // consider page alignment for grow in table. + SwLayoutFrame* pLayoutFrameToGrow = nullptr; + if ( bBrowse && rAnchorTextFrame.IsMoveable() ) + { + if ( pUpperOfOrientFrame->IsInSct() ) + { + pLayoutFrameToGrow = const_cast<SwLayoutFrame*>( + pUpperOfOrientFrame->FindSctFrame()->GetUpper()); + nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(), + aRectFnSet.GetPrtBottom(*pLayoutFrameToGrow) ); + if ( nDist >= 0 ) + { + pLayoutFrameToGrow = nullptr; + } + } + else + { + pLayoutFrameToGrow = + const_cast<SwLayoutFrame*>(pUpperOfOrientFrame); + } + } + else if ( rAnchorTextFrame.IsInTab() && bGrow ) + { + pLayoutFrameToGrow = const_cast<SwLayoutFrame*>(pUpperOfOrientFrame); + } + if ( pLayoutFrameToGrow ) + { + // No need to grow the anchor cell in case the follow-text-flow object + // is wrap-though. + if (!GetAnchorFrame().IsInTab() || !DoesObjFollowsTextFlow() || !bWrapThrough) + { + pLayoutFrameToGrow->Grow( -nDist ); + } + } + } + + if ( DoesObjFollowsTextFlow() && + ( aVert.GetRelationOrient() != text::RelOrientation::PAGE_FRAME && + aVert.GetRelationOrient() != text::RelOrientation::PAGE_PRINT_AREA ) ) + { + + nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(), + aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame) ); + // #i26945# - floating screen objects, which are + // anchored inside a table, doesn't follow the text flow. But, they + // have to stay inside its layout environment. + if ( nDist < 0 && pOrientFrame->IsInTab() ) + { + // If the anchor frame is the first content of the table cell + // and has no follow, the table frame is notified, + // that the object doesn't fit into the table cell. + // Adjustment of position isn't needed in this case. + if ( pOrientFrame == &rAnchorTextFrame && + !pOrientFrame->GetFollow() && + !pOrientFrame->GetIndPrev() ) + { + const_cast<SwTabFrame*>(pOrientFrame->FindTabFrame()) + ->SetDoesObjsFit( false ); + } + else + { + SwTwips nTmpRelPosY( 0 ); + if ( aRectFnSet.IsVert() ) + nTmpRelPosY = aRelPos.X() - nDist; + else + nTmpRelPosY = aRelPos.Y() + nDist; + const SwLayoutFrame& rVertEnvironLayFrame = + aEnvOfObj.GetVertEnvironmentLayoutFrame( *pUpperOfOrientFrame ); + nTmpRelPosY = AdjustVertRelPos( nTopOfAnch, aRectFnSet.IsVert(), aRectFnSet.IsVertL2R(), + rVertEnvironLayFrame, + nTmpRelPosY, + DoesObjFollowsTextFlow(), + false ); + if ( aRectFnSet.IsVert() ) + { + aRelPos.setX( nTmpRelPosY ); + // --> OD 2009-08-31 #mongolianlayout# + if ( !aRectFnSet.IsVertL2R() ) + { + GetAnchoredObj().SetObjLeft( nTopOfAnch - + aRelPos.X() - + aObjBoundRect.Width() ); + } + else + { + GetAnchoredObj().SetObjLeft( nTopOfAnch + aRelPos.X() ); + } + } + else + { + aRelPos.setY( nTmpRelPosY ); + GetAnchoredObj().SetObjTop( nTopOfAnch + aRelPos.Y() ); + } + // If the anchor frame is the first content of the table cell + // and the object still doesn't fit, the table frame is notified, + // that the object doesn't fit into the table cell. + nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(), + aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame) ); + if ( nDist < 0 && + pOrientFrame == &rAnchorTextFrame && !pOrientFrame->GetIndPrev() ) + { + const_cast<SwTabFrame*>(pOrientFrame->FindTabFrame()) + ->SetDoesObjsFit( false ); + } + } + } + // Don't move split flys around for follow text flow purposes; if they don't fit their + // parent anymore, they will shrink and part of the content will move to the follow fly. + else if (!bSplitFly) + { + // follow text flow + const bool bInFootnote = rAnchorTextFrame.IsInFootnote(); + while( bMoveable && nDist < 0 ) + { + bool bInSct = pUpperOfOrientFrame->IsInSct(); + if ( bInSct ) + { + const SwLayoutFrame* pTmp = pUpperOfOrientFrame->FindSctFrame()->GetUpper(); + nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(), + aRectFnSet.GetPrtBottom(*pTmp) ); + // #i23129# - Try to flow into next + // section|section column. Thus, do *not* leave section + // area, if anchored object doesn't fit into upper of section. + // But the anchored object is allowed to overlap bottom + // section|section column. + if ( nDist >= 0 ) + { + break; + } + } + if ( !bInSct && + aRectFnSet.GetTop(GetAnchoredObj().GetObjRect()) == + aRectFnSet.GetPrtTop(*pUpperOfOrientFrame) ) + // It doesn't fit, moving it would not help either anymore + break; + + const SwLayoutFrame* pNextLay = pUpperOfOrientFrame->GetLeaf( + ( bInSct + ? MAKEPAGE_NOSECTION + : ( bInFootnote ? MAKEPAGE_NONE : MAKEPAGE_APPEND ) ), + true, &rAnchorTextFrame ); + // correction: + // If anchor is in footnote and proposed next layout environment + // isn't a footnote frame, object can't follow the text flow + if ( bInFootnote && pNextLay && !pNextLay->IsFootnoteFrame() ) + { + pNextLay = nullptr; + } + if ( pNextLay ) + { + SwRectFnSet fnRectX(pNextLay); + if ( !bInSct || + ( pUpperOfOrientFrame->FindSctFrame()->IsAnFollow( pNextLay->FindSctFrame() ) && + fnRectX.GetHeight(pNextLay->getFramePrintArea()) ) ) + { + SwTwips nTmpRelPosY = + aRectFnSet.YDiff( aRectFnSet.GetPrtTop(*pNextLay), + nTopOfAnch ); + if ( aRectFnSet.IsVert() ) + aRelPos.setX( nTmpRelPosY ); + else + aRelPos.setY( nTmpRelPosY ); + pUpperOfOrientFrame = pNextLay; + aRectFnSet.Refresh(pUpperOfOrientFrame); + bMoveable = rAnchorTextFrame.IsMoveable( pUpperOfOrientFrame ); + if( fnRectX.IsVert() ) + { + // --> OD 2009-08-31 #mongolianlayout# + if ( !aRectFnSet.IsVertL2R() ) + { + GetAnchoredObj().SetObjLeft( nTopOfAnch - + aRelPos.X() - + aObjBoundRect.Width() ); + } + else + { + GetAnchoredObj().SetObjLeft( nTopOfAnch + + aRelPos.X() ); + } + } + else + GetAnchoredObj().SetObjTop( nTopOfAnch + + aRelPos.Y() ); + nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(), + aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame) ); + } + // #i23129# - leave section area + else if ( bInSct ) + { + const SwLayoutFrame* pTmp = pUpperOfOrientFrame->FindSctFrame()->GetUpper(); + nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(), + aRectFnSet.GetPrtBottom(*pTmp) ); + if( nDist < 0 ) + pUpperOfOrientFrame = pTmp; + else + break; + } + } + else if ( bInSct ) + { + // If we don't have enough room within the Area, we take a look at + // the Page + const SwLayoutFrame* pTmp = pUpperOfOrientFrame->FindSctFrame()->GetUpper(); + nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(), + aRectFnSet.GetPrtBottom(*pTmp) ); + if( nDist < 0 ) + pUpperOfOrientFrame = pTmp; + else + break; + } + else + bMoveable = false; + } + } + } + + // keep layout frame vertical position is oriented at. + mpVertPosOrientFrame = pUpperOfOrientFrame; + + // If it was requested to not overlap with already formatted objects, take care of that + // here. + CalcOverlap(pAnchorFrameForVertPos, aRelPos, nTopOfAnch); + } + + // determine 'horizontal' position + { + // determine horizontal positioning and alignment attributes + SwFormatHoriOrient aHori( rFrameFormat.GetHoriOrient() ); + + // set calculated vertical position in order to determine correct + // frame, the horizontal position is oriented at. + const SwTwips nTopOfAnch = GetTopForObjPos( *pAnchorFrameForVertPos, aRectFnSet.FnRect(), aRectFnSet.IsVert() ); + if( aRectFnSet.IsVert() ) + { + // --> OD 2009-08-31 #mongolianlayout# + if ( !aRectFnSet.IsVertL2R() ) + { + GetAnchoredObj().SetObjLeft( nTopOfAnch - + aRelPos.X() - aObjBoundRect.Width() ); + } + else + { + GetAnchoredObj().SetObjLeft( nTopOfAnch + aRelPos.X() ); + } + } + else + GetAnchoredObj().SetObjTop( nTopOfAnch + aRelPos.Y() ); + + // determine frame, horizontal position is oriented at. + // #i28701# - If floating screen object doesn't follow + // the text flow, its horizontal position is oriented at <pOrientFrame>. + const SwFrame* pHoriOrientFrame = DoesObjFollowsTextFlow() + ? &GetHoriVirtualAnchor( *mpVertPosOrientFrame ) + : pOrientFrame; + + // #i26791# - get 'horizontal' offset to frame anchor position. + SwTwips nHoriOffsetToFrameAnchorPos( 0 ); + SwTwips nRelPosX = CalcRelPosX( *pHoriOrientFrame, aEnvOfObj, + aHori, rLR, rUL, bWrapThrough, + ( aRectFnSet.IsVert() ? aRelPos.X() : aRelPos.Y() ), + nHoriOffsetToFrameAnchorPos ); + + // #i26791# - determine offset to 'horizontal' frame + // anchor position, depending on layout-direction + if ( aRectFnSet.IsVert() ) + { + aRelPos.setY( nRelPosX ); + maOffsetToFrameAnchorPos.setY( nHoriOffsetToFrameAnchorPos ); + } + else + { + aRelPos.setX( nRelPosX ); + maOffsetToFrameAnchorPos.setX( nHoriOffsetToFrameAnchorPos ); + } + + // save calculated horizontal position - needed for filters + // (including the xml-filter) + { + SwTwips nAttrRelPosX = nRelPosX - nHoriOffsetToFrameAnchorPos; + if ( aHori.GetHoriOrient() != text::HoriOrientation::NONE && + aHori.GetPos() != nAttrRelPosX ) + { + aHori.SetPos( nAttrRelPosX ); + const_cast<SwFrameFormat&>(rFrameFormat).LockModify(); + const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr( aHori ); + const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify(); + } + } + } + + // set absolute position at object + const SwTwips nTopOfAnch = GetTopForObjPos( *pAnchorFrameForVertPos, aRectFnSet.FnRect(), aRectFnSet.IsVert() ); + if( aRectFnSet.IsVert() ) + { + // --> OD 2009-08-31 #mongolianlayout# + if ( !aRectFnSet.IsVertL2R() ) + { + GetAnchoredObj().SetObjLeft( nTopOfAnch - + aRelPos.X() - aObjBoundRect.Width() ); + } + else + { + GetAnchoredObj().SetObjLeft( nTopOfAnch + aRelPos.X() ); + } + GetAnchoredObj().SetObjTop( rAnchorTextFrame.getFrameArea().Top() + + aRelPos.Y() ); + } + else + { + GetAnchoredObj().SetObjLeft( rAnchorTextFrame.getFrameArea().Left() + + aRelPos.X() ); + GetAnchoredObj().SetObjTop( nTopOfAnch + aRelPos.Y() ); + } + + // set relative position at object + GetAnchoredObj().SetCurrRelPos( aRelPos ); +} + +void SwToContentAnchoredObjectPosition::CalcOverlap(const SwTextFrame* pAnchorFrameForVertPos, + Point& rRelPos, const SwTwips nTopOfAnch) +{ + const SwFrameFormat& rFrameFormat = GetFrameFormat(); + bool bAllowOverlap = rFrameFormat.GetWrapInfluenceOnObjPos().GetAllowOverlap(); + if (bAllowOverlap) + { + return; + } + + if (rFrameFormat.GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH) + { + // This is explicit wrap through: allowed to overlap. + return; + } + + if (SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT)) + { + // This is the frame part of a textbox, just take the offset from the textbox's shape part. + SwFrameFormat* pShapeOfTextBox + = SwTextBoxHelper::getOtherTextBoxFormat(&rFrameFormat, RES_FLYFRMFMT); + if (pShapeOfTextBox) + { + SwTwips nYDiff = pShapeOfTextBox->GetWrapInfluenceOnObjPos().GetOverlapVertOffset(); + if (nYDiff > 0) + { + rRelPos.setY(rRelPos.getY() + nYDiff + 1); + GetAnchoredObj().SetObjTop(nTopOfAnch + rRelPos.Y()); + } + } + return; + } + + // Get the list of objects. + auto pSortedObjs = pAnchorFrameForVertPos->GetDrawObjs(); + const SwLayoutFrame* pAnchorUpper = pAnchorFrameForVertPos->GetUpper(); + + bool bSplitFly = false; + SwFlyFrame* pFlyFrame = GetAnchoredObj().DynCastFlyFrame(); + if (pFlyFrame && pFlyFrame->IsFlySplitAllowed()) + { + // At least for split flys we need to consider objects on the same page, but anchored in + // different text frames. + bSplitFly = true; + + SwFrame* pFlyFrameAnchor = pFlyFrame->GetAnchorFrameContainingAnchPos(); + if (pFlyFrameAnchor && pFlyFrameAnchor->IsInFly()) + { + // An inner fly overlapping with its outer fly is fine. + return; + } + + const SwPageFrame* pPageFrame = pAnchorFrameForVertPos->FindPageFrame(); + if (pPageFrame) + { + pSortedObjs = pPageFrame->GetSortedObjs(); + } + } + + if (!pSortedObjs) + { + return; + } + + for (const auto& pAnchoredObj : *pSortedObjs) + { + if (pAnchoredObj == &GetAnchoredObj()) + { + // We found ourselves, stop iterating. + break; + } + + if (SwTextBoxHelper::isTextBox(&pAnchoredObj->GetFrameFormat(), RES_FLYFRMFMT)) + { + // Overlapping with the frame of a textbox is fine. + continue; + } + + SwFlyFrame* pAnchoredObjFly = pAnchoredObj->DynCastFlyFrame(); + if (bSplitFly) + { + if (!pAnchoredObjFly) + { + // This is a split fly, then overlap is only checked against other split flys. + continue; + } + + if (pAnchoredObjFly->getRootFrame()->IsInFlyDelList(pAnchoredObjFly)) + { + // A fly overlapping with a to-be-deleted fly is fine. + continue; + } + + SwFrame* pAnchoredObjFlyAnchor = pAnchoredObjFly->GetAnchorFrameContainingAnchPos(); + if (pAnchoredObjFlyAnchor && pAnchoredObjFlyAnchor->IsInFly()) + { + // An inner fly overlapping with its outer fly is fine. + continue; + } + + if (pAnchoredObjFlyAnchor && pAnchoredObjFlyAnchor->GetUpper() != pAnchorUpper) + { + // A fly overlapping with a fly from another upper is fine. + continue; + } + } + + css::text::WrapTextMode eWrap = pAnchoredObj->GetFrameFormat().GetSurround().GetSurround(); + if (eWrap == css::text::WrapTextMode_THROUGH) + { + // The other object is wrap through: allowed to overlap. + continue; + } + + if (!GetAnchoredObj().GetObjRect().Overlaps(pAnchoredObj->GetObjRect())) + { + // Found an already positioned object, but it doesn't overlap, ignore. + continue; + } + + // Already formatted, overlaps: resolve the conflict by shifting ourselves down. + SwTwips nYDiff = pAnchoredObj->GetObjRect().Bottom() - GetAnchoredObj().GetObjRect().Top(); + rRelPos.setY(rRelPos.getY() + nYDiff + 1); + GetAnchoredObj().SetObjTop(nTopOfAnch + rRelPos.Y()); + + // Store our offset that avoids the overlap. If this is a shape of a textbox, then the frame + // of the textbox will use it. + SwFormatWrapInfluenceOnObjPos aInfluence(rFrameFormat.GetWrapInfluenceOnObjPos()); + aInfluence.SetOverlapVertOffset(nYDiff); + const_cast<SwFrameFormat&>(rFrameFormat).LockModify(); + const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr(aInfluence); + const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify(); + } +} + +/** + * Determine frame for horizontal position + */ +const SwFrame& SwToContentAnchoredObjectPosition::GetHoriVirtualAnchor( + const SwLayoutFrame& _rProposedFrame ) const +{ + const SwFrame* pHoriVirtAnchFrame = &_rProposedFrame; + + // Search for first lower content frame, which is the anchor or a follow + // of the anchor (Note: <Anchor.IsAnFollow( Anchor )> is true) + // If none found, <_rProposedFrame> is returned. + const SwFrame* pFrame = _rProposedFrame.Lower(); + while ( pFrame ) + { + if ( pFrame->IsContentFrame() && + GetAnchorTextFrame().IsAnFollow( static_cast<const SwContentFrame*>(pFrame) ) ) + { + pHoriVirtAnchFrame = pFrame; + break; + } + pFrame = pFrame->GetNext(); + } + + return *pHoriVirtAnchFrame; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/objectpositioning/tolayoutanchoredobjectposition.cxx b/sw/source/core/objectpositioning/tolayoutanchoredobjectposition.cxx new file mode 100644 index 0000000000..3e7d98bf7a --- /dev/null +++ b/sw/source/core/objectpositioning/tolayoutanchoredobjectposition.cxx @@ -0,0 +1,226 @@ +/* -*- 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 <tolayoutanchoredobjectposition.hxx> +#include <anchoredobject.hxx> +#include <frame.hxx> +#include <pagefrm.hxx> +#include <svx/svdobj.hxx> +#include <frmfmt.hxx> +#include <fmtanchr.hxx> +#include <fmtornt.hxx> +#include <fmtsrnd.hxx> +#include <frmatr.hxx> +#include <viewsh.hxx> +#include <viewopt.hxx> +#include <rootfrm.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> + +using namespace ::com::sun::star; + +namespace objectpositioning +{ +SwToLayoutAnchoredObjectPosition::SwToLayoutAnchoredObjectPosition( SdrObject& _rDrawObj ) + : SwAnchoredObjectPosition( _rDrawObj ) +{} + +SwToLayoutAnchoredObjectPosition::~SwToLayoutAnchoredObjectPosition() +{} + +/** calculate position for object position type TO_LAYOUT */ +void SwToLayoutAnchoredObjectPosition::CalcPosition() +{ + const SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() ); + + SwRectFnSet aRectFnSet(&GetAnchorFrame()); + + const SwFrameFormat& rFrameFormat = GetFrameFormat(); + const SvxLRSpaceItem &rLR = rFrameFormat.GetLRSpace(); + const SvxULSpaceItem &rUL = rFrameFormat.GetULSpace(); + + const bool bFlyAtFly = RndStdIds::FLY_AT_FLY == rFrameFormat.GetAnchor().GetAnchorId(); + + // determine position. + // 'vertical' and 'horizontal' position are calculated separately + Point aRelPos; + + // calculate 'vertical' position + SwFormatVertOrient aVert( rFrameFormat.GetVertOrient() ); + { + // to-frame anchored objects are *only* vertical positioned centered or + // bottom, if its wrap mode is 'through' and its anchor frame has fixed + // size. Otherwise, it's positioned top. + sal_Int16 eVertOrient = aVert.GetVertOrient(); + if ( bFlyAtFly && + ( eVertOrient == text::VertOrientation::CENTER || + eVertOrient == text::VertOrientation::BOTTOM ) && + css::text::WrapTextMode_THROUGH != rFrameFormat.GetSurround().GetSurround() && + !GetAnchorFrame().HasFixSize() ) + { + eVertOrient = text::VertOrientation::TOP; + } + // #i26791# - get vertical offset to frame anchor position. + SwTwips nVertOffsetToFrameAnchorPos( 0 ); + SwTwips nRelPosY = + GetVertRelPos( GetAnchorFrame(), GetAnchorFrame(), eVertOrient, + aVert.GetRelationOrient(), aVert.GetPos(), + rLR, rUL, nVertOffsetToFrameAnchorPos ); + + // keep the calculated relative vertical position - needed for filters + // (including the xml-filter) + { + SwTwips nAttrRelPosY = nRelPosY - nVertOffsetToFrameAnchorPos; + if ( aVert.GetVertOrient() != text::VertOrientation::NONE && + aVert.GetPos() != nAttrRelPosY ) + { + aVert.SetPos( nAttrRelPosY ); + const_cast<SwFrameFormat&>(rFrameFormat).LockModify(); + const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr( aVert ); + const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify(); + } + } + + // determine absolute 'vertical' position, depending on layout-direction + // #i26791# - determine offset to 'vertical' frame + // anchor position, depending on layout-direction + if( aRectFnSet.IsVert() ) + { + if ( aRectFnSet.IsVertL2R() ) + aRelPos.setX( nRelPosY ); + else + aRelPos.setX( -nRelPosY - aObjBoundRect.Width() ); + maOffsetToFrameAnchorPos.setX( nVertOffsetToFrameAnchorPos ); + } + else + { + aRelPos.setY( nRelPosY ); + maOffsetToFrameAnchorPos.setY( nVertOffsetToFrameAnchorPos ); + } + + // if in online-layout the bottom of to-page anchored object is beyond + // the page bottom, the page frame has to grow by growing its body frame. + const SwViewShell *pSh = GetAnchorFrame().getRootFrame()->GetCurrShell(); + if ( !bFlyAtFly && GetAnchorFrame().IsPageFrame() && + pSh && pSh->GetViewOptions()->getBrowseMode() ) + { + const tools::Long nAnchorBottom = GetAnchorFrame().getFrameArea().Bottom(); + const tools::Long nBottom = GetAnchorFrame().getFrameArea().Top() + + aRelPos.Y() + aObjBoundRect.Height(); + if ( nAnchorBottom < nBottom ) + { + static_cast<SwPageFrame&>(GetAnchorFrame()). + FindBodyCont()->Grow( nBottom - nAnchorBottom ); + } + } + } // end of determination of vertical position + + // calculate 'horizontal' position + SwFormatHoriOrient aHori( rFrameFormat.GetHoriOrient() ); + { + // consider toggle of horizontal position for even pages. + const bool bToggle = aHori.IsPosToggle() && + !GetAnchorFrame().FindPageFrame()->OnRightPage(); + sal_Int16 eHoriOrient = aHori.GetHoriOrient(); + sal_Int16 eRelOrient = aHori.GetRelationOrient(); + // toggle orientation + ToggleHoriOrientAndAlign( bToggle, eHoriOrient, eRelOrient ); + + // determine alignment values: + // <nWidth>: 'width' of the alignment area + // <nOffset>: offset of alignment area, relative to 'left' of + // frame anchor position + SwTwips nWidth, nOffset; + { + bool bDummy; // in this context irrelevant output parameter + GetHoriAlignmentValues( GetAnchorFrame(), GetAnchorFrame(), + eRelOrient, false, + nWidth, nOffset, bDummy ); + } + + SwTwips nObjWidth = aRectFnSet.GetWidth(aObjBoundRect); + + // determine relative horizontal position + SwTwips nRelPosX; + if ( text::HoriOrientation::NONE == eHoriOrient ) + { + if( bToggle || + ( !aHori.IsPosToggle() && GetAnchorFrame().IsRightToLeft() ) ) + { + nRelPosX = nWidth - nObjWidth - aHori.GetPos(); + } + else + { + nRelPosX = aHori.GetPos(); + } + } + else if ( text::HoriOrientation::CENTER == eHoriOrient ) + nRelPosX = (nWidth / 2) - (nObjWidth / 2); + else if ( text::HoriOrientation::RIGHT == eHoriOrient ) + nRelPosX = nWidth - ( nObjWidth + + ( aRectFnSet.IsVert() ? rUL.GetLower() : rLR.GetRight() ) ); + else + nRelPosX = aRectFnSet.IsVert() ? rUL.GetUpper() : rLR.GetLeft(); + nRelPosX += nOffset; + + // no 'negative' relative horizontal position + // OD 06.11.2003 #FollowTextFlowAtFrame# - negative positions allow for + // to frame anchored objects. + if ( !bFlyAtFly && nRelPosX < 0 ) + { + nRelPosX = 0; + } + + // determine absolute 'horizontal' position, depending on layout-direction + // #i26791# - determine offset to 'horizontal' frame + // anchor position, depending on layout-direction + if( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() ) + { + + aRelPos.setY( nRelPosX ); + maOffsetToFrameAnchorPos.setY( nOffset ); + } + else + { + aRelPos.setX( nRelPosX ); + maOffsetToFrameAnchorPos.setX( nOffset ); + } + + // keep the calculated relative horizontal position - needed for filters + // (including the xml-filter) + { + SwTwips nAttrRelPosX = nRelPosX - nOffset; + if ( text::HoriOrientation::NONE != aHori.GetHoriOrient() && + aHori.GetPos() != nAttrRelPosX ) + { + aHori.SetPos( nAttrRelPosX ); + const_cast<SwFrameFormat&>(rFrameFormat).LockModify(); + const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr( aHori ); + const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify(); + } + } + } // end of determination of horizontal position + + // keep calculate relative position + maRelPos = aRelPos; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |