diff options
Diffstat (limited to 'sw/source/core/draw')
-rw-r--r-- | sw/source/core/draw/dcontact.cxx | 2632 | ||||
-rw-r--r-- | sw/source/core/draw/dflyobj.cxx | 1295 | ||||
-rw-r--r-- | sw/source/core/draw/dobjfac.cxx | 39 | ||||
-rw-r--r-- | sw/source/core/draw/dpage.cxx | 262 | ||||
-rw-r--r-- | sw/source/core/draw/drawdoc.cxx | 137 | ||||
-rw-r--r-- | sw/source/core/draw/dview.cxx | 1019 |
6 files changed, 5384 insertions, 0 deletions
diff --git a/sw/source/core/draw/dcontact.cxx b/sw/source/core/draw/dcontact.cxx new file mode 100644 index 000000000..54e156710 --- /dev/null +++ b/sw/source/core/draw/dcontact.cxx @@ -0,0 +1,2632 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <hintids.hxx> +#include <svx/svdpage.hxx> +#include <svx/svditer.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdviter.hxx> +#include <svx/svdview.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/shapepropertynotifier.hxx> +#include <drawdoc.hxx> +#include <fmtornt.hxx> +#include <viewimp.hxx> +#include <fmtsrnd.hxx> +#include <fmtanchr.hxx> +#include <node.hxx> +#include <fmtcntnt.hxx> +#include <fmtfsize.hxx> +#include <pam.hxx> +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <frmtool.hxx> +#include <flyfrm.hxx> +#include <textboxhelper.hxx> +#include <frmfmt.hxx> +#include <fmtfollowtextflow.hxx> +#include <dflyobj.hxx> +#include <dcontact.hxx> +#include <unodraw.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentState.hxx> +#include <IDocumentUndoRedo.hxx> +#include <doc.hxx> +#include <hints.hxx> +#include <txtfrm.hxx> +#include <frameformats.hxx> +#include <sortedobjs.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <svx/sdr/contact/viewcontactofvirtobj.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <svx/sdr/contact/viewobjectcontactofsdrobj.hxx> +#include <com/sun/star/text/WritingMode2.hpp> +#include <calbck.hxx> +#include <algorithm> +#include <txtfly.hxx> +#include <sal/log.hxx> + +using namespace ::com::sun::star; + +namespace +{ + /** unary function used to find a 'virtual' drawing object anchored at a given frame */ + struct VirtObjAnchoredAtFramePred + { + const SwFrame* m_pAnchorFrame; + + // #i26791# - compare with master frame + static const SwFrame* FindFrame(const SwFrame* pFrame) + { + if(!pFrame || !pFrame->IsContentFrame()) + return pFrame; + auto pContentFrame = static_cast<const SwContentFrame*>(pFrame); + while(pContentFrame->IsFollow()) + pContentFrame = pContentFrame->FindMaster(); + return pContentFrame; + } + + VirtObjAnchoredAtFramePred(const SwFrame* pAnchorFrame) + : m_pAnchorFrame(FindFrame(pAnchorFrame)) + {} + + bool operator()(const SwDrawVirtObjPtr& rpDrawVirtObj) + { + return FindFrame(rpDrawVirtObj->GetAnchorFrame()) == m_pAnchorFrame; + } + }; +} + +void setContextWritingMode(SdrObject* pObj, SwFrame const * pAnchor) +{ + if(!pObj || !pAnchor) + return; + short nWritingDirection = + pAnchor->IsVertical() ? text::WritingMode2::TB_RL : + pAnchor->IsRightToLeft() ? text::WritingMode2::RL_TB : + text::WritingMode2::LR_TB; + pObj->SetContextWritingMode(nWritingDirection); +} + + +/** The Get reverse way: seeks the format to the specified object. + * If the object is a SwVirtFlyDrawObj then the format of this + * will be acquired. + * Otherwise it is just a simple drawing object. This has a + * UserCall and is the client of the searched format. +*/ +SwFrameFormat *FindFrameFormat( SdrObject *pObj ) +{ + SwFrameFormat* pRetval = nullptr; + + if (SwVirtFlyDrawObj* pFlyDrawObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj)) + { + pRetval = pFlyDrawObj->GetFormat(); + } + else + { + SwContact* pContact = GetUserCall(pObj); + if ( pContact ) + { + pRetval = pContact->GetFormat(); + } + } + return pRetval; +} + +bool HasWrap( const SdrObject* pObj ) +{ + if ( pObj ) + { + const SwFrameFormat* pFormat = ::FindFrameFormat( pObj ); + if ( pFormat ) + { + return css::text::WrapTextMode_THROUGH != pFormat->GetSurround().GetSurround(); + } + } + + return false; +} + +/// returns the BoundRect _inclusive_ distance of the object. +SwRect GetBoundRectOfAnchoredObj( const SdrObject* pObj ) +{ + SwRect aRet( pObj->GetCurrentBoundRect() ); + // #i68520# - call cache of <SwAnchoredObject> + SwContact* pContact( GetUserCall( pObj ) ); + if ( pContact ) + { + const SwAnchoredObject* pAnchoredObj( pContact->GetAnchoredObj( pObj ) ); + if ( pAnchoredObj ) + { + aRet = pAnchoredObj->GetObjRectWithSpaces(); + } + } + return aRet; +} + +/// Returns the UserCall if applicable from the group object +SwContact* GetUserCall( const SdrObject* pObj ) +{ + SdrObject *pTmp; + while ( !pObj->GetUserCall() && nullptr != (pTmp = pObj->getParentSdrObjectFromSdrObject()) ) + pObj = pTmp; + assert((!pObj->GetUserCall() || nullptr != dynamic_cast<const SwContact*>(pObj->GetUserCall())) && + "<::GetUserCall(..)> - wrong type of found object user call." ); + return static_cast<SwContact*>(pObj->GetUserCall()); +} + +/// Returns true if the SrdObject is a Marquee-Object (scrolling text) +bool IsMarqueeTextObj( const SdrObject& rObj ) +{ + if (SdrInventor::Default != rObj.GetObjInventor() || + SdrObjKind::Text != rObj.GetObjIdentifier()) + return false; + SdrTextAniKind eTKind = static_cast<const SdrTextObj&>(rObj).GetTextAniKind(); + return ( SdrTextAniKind::Scroll == eTKind + || SdrTextAniKind::Alternate == eTKind || SdrTextAniKind::Slide == eTKind ); +} + +SwContact::SwContact( SwFrameFormat *pToRegisterIn ) : + SwClient( pToRegisterIn ), + mbInDTOR( false ) +{} + +SwContact::~SwContact() +{ + SetInDTOR(); +} + + +void SwContact::SetInDTOR() +{ + mbInDTOR = true; +} + +/// method to move drawing object to corresponding visible layer +void SwContact::MoveObjToVisibleLayer( SdrObject* _pDrawObj ) +{ + // #i46297# - notify background about the arriving of + // the object and invalidate its position. + const bool bNotify( !GetFormat()->getIDocumentDrawModelAccess().IsVisibleLayerId( _pDrawObj->GetLayer() ) ); + + MoveObjToLayer( true, _pDrawObj ); + + // #i46297# + if ( !bNotify ) + return; + + SwAnchoredObject* pAnchoredObj = GetAnchoredObj( _pDrawObj ); + assert(pAnchoredObj); + ::setContextWritingMode( _pDrawObj, pAnchoredObj->GetAnchorFrameContainingAnchPos() ); + // Note: as-character anchored objects aren't registered at a page frame and + // a notification of its background isn't needed. + if ( pAnchoredObj->GetPageFrame() ) + { + ::Notify_Background( _pDrawObj, pAnchoredObj->GetPageFrame(), + pAnchoredObj->GetObjRect(), PrepareHint::FlyFrameArrive, true ); + } + + pAnchoredObj->InvalidateObjPos(); +} + +/// method to move drawing object to corresponding invisible layer - #i18447# +void SwContact::MoveObjToInvisibleLayer( SdrObject* _pDrawObj ) +{ + // #i46297# - notify background about the leaving of the object. + const bool bNotify( GetFormat()->getIDocumentDrawModelAccess().IsVisibleLayerId( _pDrawObj->GetLayer() ) ); + + MoveObjToLayer( false, _pDrawObj ); + + // #i46297# + if ( bNotify ) + { + SwAnchoredObject* pAnchoredObj = GetAnchoredObj( _pDrawObj ); + assert(pAnchoredObj); + // Note: as-character anchored objects aren't registered at a page frame and + // a notification of its background isn't needed. + if (pAnchoredObj->GetPageFrame()) + { + ::Notify_Background( _pDrawObj, pAnchoredObj->GetPageFrame(), + pAnchoredObj->GetObjRect(), PrepareHint::FlyFrameLeave, true ); + } + } +} + +/** method to move object to visible/invisible layer - #i18447# + + implementation for the public method <MoveObjToVisibleLayer(..)> + and <MoveObjToInvisibleLayer(..)> +*/ +void SwContact::MoveObjToLayer( const bool _bToVisible, + SdrObject* _pDrawObj ) +{ + if ( !_pDrawObj ) + { + OSL_FAIL( "SwDrawContact::MoveObjToLayer(..) - no drawing object!" ); + return; + } + + if ( !GetRegisteredIn() ) + { + OSL_FAIL( "SwDrawContact::MoveObjToLayer(..) - no drawing frame format!" ); + return; + } + + const IDocumentDrawModelAccess& rIDDMA = static_cast<SwFrameFormat*>(GetRegisteredInNonConst())->getIDocumentDrawModelAccess(); + + SdrLayerID nToHellLayerId = + _bToVisible ? rIDDMA.GetHellId() : rIDDMA.GetInvisibleHellId(); + SdrLayerID nToHeavenLayerId = + _bToVisible ? rIDDMA.GetHeavenId() : rIDDMA.GetInvisibleHeavenId(); + SdrLayerID nToControlLayerId = + _bToVisible ? rIDDMA.GetControlsId() : rIDDMA.GetInvisibleControlsId(); + SdrLayerID nFromHellLayerId = + _bToVisible ? rIDDMA.GetInvisibleHellId() : rIDDMA.GetHellId(); + SdrLayerID nFromHeavenLayerId = + _bToVisible ? rIDDMA.GetInvisibleHeavenId() : rIDDMA.GetHeavenId(); + SdrLayerID nFromControlLayerId = + _bToVisible ? rIDDMA.GetInvisibleControlsId() : rIDDMA.GetControlsId(); + + if ( dynamic_cast<const SdrObjGroup*>( _pDrawObj) != nullptr ) + { + // determine layer for group object + { + // proposed layer of a group object is the hell layer + SdrLayerID nNewLayerId = nToHellLayerId; + if ( ::CheckControlLayer( _pDrawObj ) ) + { + // it has to be the control layer, if one of the member + // is a control + nNewLayerId = nToControlLayerId; + } + else if ( _pDrawObj->GetLayer() == rIDDMA.GetHeavenId() || + _pDrawObj->GetLayer() == rIDDMA.GetInvisibleHeavenId() ) + { + // it has to be the heaven layer, if method <GetLayer()> reveals + // a heaven layer + nNewLayerId = nToHeavenLayerId; + } + // set layer at group object, but do *not* broadcast and + // no propagation to the members. + // Thus, call <NbcSetLayer(..)> at super class + _pDrawObj->SdrObject::NbcSetLayer( nNewLayerId ); + } + + // call method recursively for group object members + const SdrObjList* pLst = + static_cast<SdrObjGroup*>(_pDrawObj)->GetSubList(); + if ( pLst ) + { + for ( size_t i = 0; i < pLst->GetObjCount(); ++i ) + { + MoveObjToLayer( _bToVisible, pLst->GetObj( i ) ); + } + } + } + else + { + const SdrLayerID nLayerIdOfObj = _pDrawObj->GetLayer(); + if ( nLayerIdOfObj == nFromHellLayerId ) + { + _pDrawObj->SetLayer( nToHellLayerId ); + } + else if ( nLayerIdOfObj == nFromHeavenLayerId ) + { + _pDrawObj->SetLayer( nToHeavenLayerId ); + } + else if ( nLayerIdOfObj == nFromControlLayerId ) + { + _pDrawObj->SetLayer( nToControlLayerId ); + } + } +} + +/// get minimum order number of anchored objects handled by with contact +sal_uInt32 SwContact::GetMinOrdNum() const +{ + sal_uInt32 nMinOrdNum( SAL_MAX_UINT32 ); + + std::vector< SwAnchoredObject* > aObjs; + GetAnchoredObjs( aObjs ); + + while ( !aObjs.empty() ) + { + sal_uInt32 nTmpOrdNum = aObjs.back()->GetDrawObj()->GetOrdNum(); + + if ( nTmpOrdNum < nMinOrdNum ) + { + nMinOrdNum = nTmpOrdNum; + } + + aObjs.pop_back(); + } + + OSL_ENSURE( nMinOrdNum != SAL_MAX_UINT32, + "<SwContact::GetMinOrdNum()> - no order number found." ); + return nMinOrdNum; +} + +/// get maximum order number of anchored objects handled by with contact +sal_uInt32 SwContact::GetMaxOrdNum() const +{ + sal_uInt32 nMaxOrdNum( 0 ); + + std::vector< SwAnchoredObject* > aObjs; + GetAnchoredObjs( aObjs ); + + while ( !aObjs.empty() ) + { + sal_uInt32 nTmpOrdNum = aObjs.back()->GetDrawObj()->GetOrdNum(); + + if ( nTmpOrdNum > nMaxOrdNum ) + { + nMaxOrdNum = nTmpOrdNum; + } + + aObjs.pop_back(); + } + + return nMaxOrdNum; +} + +namespace +{ + Point lcl_GetWW8Pos(SwAnchoredObject const * pAnchoredObj, const bool bFollowTextFlow, sw::WW8AnchorConv& reConv) + { + switch(reConv) + { + case sw::WW8AnchorConv::CONV2PG: + { + bool bRelToTableCell(false); + Point aPos(pAnchoredObj->GetRelPosToPageFrame(bFollowTextFlow, bRelToTableCell)); + if(bRelToTableCell) + reConv = sw::WW8AnchorConv::RELTOTABLECELL; + return aPos; + } + case sw::WW8AnchorConv::CONV2COL_OR_PARA: + return pAnchoredObj->GetRelPosToAnchorFrame(); + case sw::WW8AnchorConv::CONV2CHAR: + return pAnchoredObj->GetRelPosToChar(); + case sw::WW8AnchorConv::CONV2LINE: + return pAnchoredObj->GetRelPosToLine(); + default: ; + } + return Point(); + } +} +void SwContact::SwClientNotify(const SwModify& rMod, const SfxHint& rHint) +{ + // this does not call SwClient::SwClientNotify and thus doesn't handle RES_OBJECTDYING as usual. Is this intentional? + if (auto pFindSdrObjectHint = dynamic_cast<const sw::FindSdrObjectHint*>(&rHint)) + { + if(!pFindSdrObjectHint->m_rpObject) + pFindSdrObjectHint->m_rpObject = GetMaster(); + } + else if (auto pWW8AnchorConvHint = dynamic_cast<const sw::WW8AnchorConvHint*>(&rHint)) + { + // determine anchored object + SwAnchoredObject* pAnchoredObj(nullptr); + { + std::vector<SwAnchoredObject*> aAnchoredObjs; + GetAnchoredObjs(aAnchoredObjs); + if(!aAnchoredObjs.empty()) + pAnchoredObj = aAnchoredObjs.front(); + } + // no anchored object found. Thus, the needed layout information can't + // be determined. --> no conversion + if(!pAnchoredObj) + return; + // no conversion for anchored drawing object, which aren't attached to an + // anchor frame. + // This is the case for drawing objects, which are anchored inside a page + // header/footer of an *unused* page style. + if(dynamic_cast<SwAnchoredDrawObject*>(pAnchoredObj) && !pAnchoredObj->GetAnchorFrame()) + return; + const bool bFollowTextFlow = static_cast<const SwFrameFormat&>(rMod).GetFollowTextFlow().GetValue(); + sw::WW8AnchorConvResult& rResult(pWW8AnchorConvHint->m_rResult); + // No distinction between layout directions, because of missing + // information about WW8 in vertical layout. + rResult.m_aPos.setX(lcl_GetWW8Pos(pAnchoredObj, bFollowTextFlow, rResult.m_eHoriConv).getX()); + rResult.m_aPos.setY(lcl_GetWW8Pos(pAnchoredObj, bFollowTextFlow, rResult.m_eVertConv).getY()); + rResult.m_bConverted = true; + } +} + + +SwFlyDrawContact::SwFlyDrawContact( + SwFlyFrameFormat *pToRegisterIn, + SdrModel& rTargetModel) +: SwContact(pToRegisterIn), + mpMasterObj(new SwFlyDrawObj(rTargetModel)) +{ + // #i26791# - class <SwFlyDrawContact> contains the 'master' + // drawing object of type <SwFlyDrawObj> on its own. + mpMasterObj->SetOrdNum( 0xFFFFFFFE ); + mpMasterObj->SetUserCall( this ); +} + +SwFlyDrawContact::~SwFlyDrawContact() +{ + if ( mpMasterObj ) + { + mpMasterObj->SetUserCall( nullptr ); + if ( mpMasterObj->getSdrPageFromSdrObject() ) + mpMasterObj->getSdrPageFromSdrObject()->RemoveObject( mpMasterObj->GetOrdNum() ); + } +} + +sal_uInt32 SwFlyDrawContact::GetOrdNumForNewRef(const SwFlyFrame* pFly, + SwFrame const& rAnchorFrame) +{ + // maintain invariant that a shape's textbox immediately follows the shape + // also for the multiple SdrVirtObj created for shapes in header/footer + if (SwFrameFormat const*const pDrawFormat = + SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT)) + { + // assume that the draw SdrVirtObj is always created before the flyframe one + if (SwSortedObjs const*const pObjs = rAnchorFrame.GetDrawObjs()) + { + for (SwAnchoredObject const*const pAnchoredObj : *pObjs) + { + if (&pAnchoredObj->GetFrameFormat() == pDrawFormat) + { + return pAnchoredObj->GetDrawObj()->GetOrdNum() + 1; + } + } + } + // if called from AppendObjs(), this is a problem; if called from lcl_SetFlyFrameAttr() it's not + SAL_INFO("sw", "GetOrdNumForNewRef: cannot find SdrObject for text box's shape"); + } + // search for another Writer fly frame registered at same frame format + SwIterator<SwFlyFrame,SwFormat> aIter(*GetFormat()); + const SwFlyFrame* pFlyFrame(nullptr); + for(pFlyFrame = aIter.First(); pFlyFrame; pFlyFrame = aIter.Next()) + { + if(pFlyFrame != pFly) + break; + } + + if(pFlyFrame) + { + // another Writer fly frame found. Take its order number + return pFlyFrame->GetVirtDrawObj()->GetOrdNum(); + } + // no other Writer fly frame found. Take order number of 'master' object + // #i35748# - use method <GetOrdNumDirect()> instead + // of method <GetOrdNum()> to avoid a recalculation of the order number, + // which isn't intended. + return GetMaster()->GetOrdNumDirect(); +} + +SwVirtFlyDrawObj* SwFlyDrawContact::CreateNewRef(SwFlyFrame* pFly, + SwFlyFrameFormat* pFormat, SwFrame const& rAnchorFrame) +{ + // Find ContactObject from the Format. If there's already one, we just + // need to create a new Ref, else we create the Contact now. + + IDocumentDrawModelAccess& rIDDMA = pFormat->getIDocumentDrawModelAccess(); + SwFlyDrawContact* pContact = pFormat->GetOrCreateContact(); + SwVirtFlyDrawObj* pDrawObj( + new SwVirtFlyDrawObj( + pContact->GetMaster()->getSdrModelFromSdrObject(), + *pContact->GetMaster(), + pFly)); + pDrawObj->SetUserCall(pContact); + + // The Reader creates the Masters and inserts them into the Page in + // order to transport the z-order. + // After creating the first Reference the Masters are removed from the + // List and are not important anymore. + SdrPage* pPg = pContact->GetMaster()->getSdrPageFromSdrObject(); + if(nullptr != pPg) + { + const size_t nOrdNum = pContact->GetMaster()->GetOrdNum(); + pPg->ReplaceObject(pDrawObj, nOrdNum); + } + // #i27030# - insert new <SwVirtFlyDrawObj> instance + // into drawing page with correct order number + else + rIDDMA.GetDrawModel()->GetPage(0)->InsertObject(pDrawObj, pContact->GetOrdNumForNewRef(pFly, rAnchorFrame)); + // #i38889# - assure, that new <SwVirtFlyDrawObj> instance + // is in a visible layer. + pContact->MoveObjToVisibleLayer(pDrawObj); + return pDrawObj; +} + +// #i26791# +const SwAnchoredObject* SwFlyDrawContact::GetAnchoredObj(const SdrObject* pSdrObj) const +{ + assert(pSdrObj); + assert(dynamic_cast<const SwVirtFlyDrawObj*>(pSdrObj) != nullptr); + assert(GetUserCall(pSdrObj) == this && + "<SwFlyDrawContact::GetAnchoredObj(..)> - provided object doesn't belong to this contact"); + + const SwAnchoredObject *const pRetAnchoredObj = + static_cast<const SwVirtFlyDrawObj*>(pSdrObj)->GetFlyFrame(); + + return pRetAnchoredObj; +} + +SwAnchoredObject* SwFlyDrawContact::GetAnchoredObj(SdrObject *const pSdrObj) +{ + return const_cast<SwAnchoredObject *>(const_cast<SwFlyDrawContact const*>(this)->GetAnchoredObj(pSdrObj)); +} + +SdrObject* SwFlyDrawContact::GetMaster() +{ + return mpMasterObj.get(); +} + +/** + * @note Overriding method to control Writer fly frames, which are linked, and + * to assure that all objects anchored at/inside the Writer fly frame are + * also made visible. + */ +void SwFlyDrawContact::MoveObjToVisibleLayer( SdrObject* _pDrawObj ) +{ + assert(dynamic_cast<const SwVirtFlyDrawObj*>(_pDrawObj) != nullptr); + + if ( GetFormat()->getIDocumentDrawModelAccess().IsVisibleLayerId( _pDrawObj->GetLayer() ) ) + { + // nothing to do + return; + } + + SwFlyFrame* pFlyFrame = static_cast<SwVirtFlyDrawObj*>(_pDrawObj)->GetFlyFrame(); + + // #i44464# - consider, that Writer fly frame content + // already exists - (e.g. WW8 document is inserted into an existing document). + if ( !pFlyFrame->Lower() ) + { + pFlyFrame->InsertColumns(); + pFlyFrame->Chain( pFlyFrame->AnchorFrame() ); + pFlyFrame->InsertCnt(); + } + if ( pFlyFrame->GetDrawObjs() ) + { + for (SwAnchoredObject* i : *pFlyFrame->GetDrawObjs()) + { + // #i28701# - consider type of objects in sorted object list. + SdrObject* pObj = i->DrawObj(); + SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall()); + pContact->MoveObjToVisibleLayer( pObj ); + } + } + + // make fly frame visible + SwContact::MoveObjToVisibleLayer( _pDrawObj ); +} + +/** + * @note Override method to control Writer fly frames, which are linked, and + * to assure that all objects anchored at/inside the Writer fly frame are + * also made invisible. + */ +void SwFlyDrawContact::MoveObjToInvisibleLayer( SdrObject* _pDrawObj ) +{ + assert(dynamic_cast<const SwVirtFlyDrawObj*>(_pDrawObj) != nullptr); + + if ( !GetFormat()->getIDocumentDrawModelAccess().IsVisibleLayerId( _pDrawObj->GetLayer() ) ) + { + // nothing to do + return; + } + + SwFlyFrame* pFlyFrame = static_cast<SwVirtFlyDrawObj*>(_pDrawObj)->GetFlyFrame(); + + pFlyFrame->Unchain(); + pFlyFrame->DeleteCnt(); + if ( pFlyFrame->GetDrawObjs() ) + { + for (SwAnchoredObject* i : *pFlyFrame->GetDrawObjs()) + { + // #i28701# - consider type of objects in sorted object list. + SdrObject* pObj = i->DrawObj(); + SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall()); + pContact->MoveObjToInvisibleLayer( pObj ); + } + } + + // make fly frame invisible + SwContact::MoveObjToInvisibleLayer( _pDrawObj ); +} + +/// get data collection of anchored objects, handled by with contact +void SwFlyDrawContact::GetAnchoredObjs( std::vector<SwAnchoredObject*>& _roAnchoredObjs ) const +{ + const SwFrameFormat* pFormat = GetFormat(); + SwFlyFrame::GetAnchoredObjects( _roAnchoredObjs, *pFormat ); +} +void SwFlyDrawContact::SwClientNotify(const SwModify& rMod, const SfxHint& rHint) +{ + SwContact::SwClientNotify(rMod, rHint); + if(auto pGetZOrdnerHint = dynamic_cast<const sw::GetZOrderHint*>(&rHint)) + { + // #i11176# + // This also needs to work when no layout exists. Thus, for + // FlyFrames an alternative method is used now in that case. + auto pFormat(dynamic_cast<const SwFrameFormat*>(&rMod)); + if (pFormat && pFormat->Which() == RES_FLYFRMFMT && !pFormat->getIDocumentLayoutAccess().GetCurrentViewShell()) + pGetZOrdnerHint->m_rnZOrder = GetMaster()->GetOrdNum(); + } +} + +// SwDrawContact + +bool CheckControlLayer( const SdrObject *pObj ) +{ + if ( SdrInventor::FmForm == pObj->GetObjInventor() ) + return true; + if (const SdrObjGroup *pObjGroup = dynamic_cast<const SdrObjGroup*>(pObj)) + { + const SdrObjList *pLst = pObjGroup->GetSubList(); + for ( size_t i = 0; i < pLst->GetObjCount(); ++i ) + { + if ( ::CheckControlLayer( pLst->GetObj( i ) ) ) + { + // #i18447# - return correct value ;-) + return true; + } + } + } + return false; +} + +SwDrawContact::SwDrawContact( SwFrameFormat* pToRegisterIn, SdrObject* pObj ) : + SwContact( pToRegisterIn ), + mbMasterObjCleared( false ), + mbDisconnectInProgress( false ), + mbUserCallActive( false ), + // Note: value of <meEventTypeOfCurrentUserCall> isn't of relevance, because + // <mbUserCallActive> is false. + meEventTypeOfCurrentUserCall( SdrUserCallType::MoveOnly ) +{ + // --> #i33909# - assure, that drawing object is inserted + // in the drawing page. + if ( !pObj->IsInserted() ) + { + pToRegisterIn->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0)-> + InsertObject( pObj, pObj->GetOrdNumDirect() ); + } + + // Controls have to be always in the Control-Layer. This is also true for + // group objects, if they contain controls. + if ( ::CheckControlLayer( pObj ) ) + { + // set layer of object to corresponding invisible layer. + pObj->SetLayer( pToRegisterIn->getIDocumentDrawModelAccess().GetInvisibleControlsId() ); + } + + // #i26791# + pObj->SetUserCall( this ); + maAnchoredDrawObj.SetDrawObj( *pObj ); + + // if there already exists an SwXShape for the object, ensure it knows about us, and the SdrObject + // #i99056# + SwXShape::AddExistingShapeToFormat( *pObj ); +} + +SwDrawContact::~SwDrawContact() +{ + SetInDTOR(); + + DisconnectFromLayout(); + + // remove 'master' from drawing page + RemoveMasterFromDrawPage(); + + // remove and destroy 'virtual' drawing objects. + RemoveAllVirtObjs(); + + if ( !mbMasterObjCleared ) + { + SdrObject* pObject = const_cast< SdrObject* >( maAnchoredDrawObj.GetDrawObj() ); + SdrObject::Free( pObject ); + } +} + +void SwDrawContact::GetTextObjectsFromFormat(std::list<SdrTextObj*>& o_rTextObjects, SwDoc& rDoc) +{ + for(auto& rpFly : *rDoc.GetSpzFrameFormats()) + { + if(dynamic_cast<const SwDrawFrameFormat*>(rpFly)) + rpFly->CallSwClientNotify(sw::CollectTextObjectsHint(o_rTextObjects)); + } +} + +// #i26791# +const SwAnchoredObject* SwDrawContact::GetAnchoredObj(const SdrObject* pSdrObj ) const +{ + // handle default parameter value + if (!pSdrObj) + { + pSdrObj = GetMaster(); + } + + assert(pSdrObj); + assert(dynamic_cast<const SwDrawVirtObj*>(pSdrObj) != nullptr || + dynamic_cast<const SdrVirtObj*>(pSdrObj) == nullptr); + assert((GetUserCall(pSdrObj) == this || + pSdrObj == GetMaster()) && + "<SwDrawContact::GetAnchoredObj(..)> - provided object doesn't belongs to this contact" ); + + const SwAnchoredObject* pRetAnchoredObj = nullptr; + + if (auto pVirtObj = dynamic_cast<const SwDrawVirtObj*>(pSdrObj)) + { + pRetAnchoredObj = &(pVirtObj->GetAnchoredObj()); + } + else + { + assert(dynamic_cast<const SdrVirtObj*>(pSdrObj) == nullptr); + pRetAnchoredObj = &maAnchoredDrawObj; + } + + return pRetAnchoredObj; +} + +SwAnchoredObject* SwDrawContact::GetAnchoredObj(SdrObject *const pSdrObj) +{ + return const_cast<SwAnchoredObject*>(const_cast<SwDrawContact const*>(this)->GetAnchoredObj(pSdrObj)); +} + +SdrObject* SwDrawContact::GetMaster() +{ + return !mbMasterObjCleared + ? maAnchoredDrawObj.DrawObj() + : nullptr; +} + +const SwFrame* SwDrawContact::GetAnchorFrame( const SdrObject* _pDrawObj ) const +{ + const SwFrame* pAnchorFrame = nullptr; + if ( !_pDrawObj || + _pDrawObj == GetMaster() || + ( !_pDrawObj->GetUserCall() && + GetUserCall( _pDrawObj ) == this ) ) + { + pAnchorFrame = maAnchoredDrawObj.GetAnchorFrame(); + } + else + { + assert(dynamic_cast<SwDrawVirtObj const*>(_pDrawObj) != nullptr); + pAnchorFrame = static_cast<const SwDrawVirtObj*>(_pDrawObj)->GetAnchorFrame(); + } + + return pAnchorFrame; +} + +SwFrame* SwDrawContact::GetAnchorFrame(SdrObject const *const pDrawObj) +{ + return const_cast<SwFrame *>(const_cast<SwDrawContact const*>(this)->GetAnchorFrame(pDrawObj)); +} + +/** add a 'virtual' drawing object to drawing page. + */ +SwDrawVirtObj* SwDrawContact::AddVirtObj(SwFrame const& rAnchorFrame) +{ + maDrawVirtObjs.push_back( + SwDrawVirtObjPtr( + new SwDrawVirtObj( + GetMaster()->getSdrModelFromSdrObject(), + *GetMaster(), + *this))); + maDrawVirtObjs.back()->AddToDrawingPage(rAnchorFrame); + return maDrawVirtObjs.back().get(); +} + +/// remove 'virtual' drawing objects and destroy them. +void SwDrawContact::RemoveAllVirtObjs() +{ + for(auto& rpDrawVirtObj : maDrawVirtObjs) + { + // remove and destroy 'virtual object' + rpDrawVirtObj->RemoveFromWriterLayout(); + rpDrawVirtObj->RemoveFromDrawingPage(); + } + maDrawVirtObjs.clear(); +} + + +/// get drawing object ('master' or 'virtual') by frame. +SdrObject* SwDrawContact::GetDrawObjectByAnchorFrame( const SwFrame& _rAnchorFrame ) +{ + SdrObject* pRetDrawObj = nullptr; + + // #i26791# - compare master frames instead of direct frames + const SwFrame* pProposedAnchorFrame = &_rAnchorFrame; + if ( pProposedAnchorFrame->IsContentFrame() ) + { + const SwContentFrame* pTmpFrame = + static_cast<const SwContentFrame*>( pProposedAnchorFrame ); + while ( pTmpFrame->IsFollow() ) + { + pTmpFrame = pTmpFrame->FindMaster(); + } + pProposedAnchorFrame = pTmpFrame; + } + + const SwFrame* pMasterObjAnchorFrame = GetAnchorFrame(); + if ( pMasterObjAnchorFrame && pMasterObjAnchorFrame->IsContentFrame() ) + { + const SwContentFrame* pTmpFrame = + static_cast<const SwContentFrame*>( pMasterObjAnchorFrame ); + while ( pTmpFrame->IsFollow() ) + { + pTmpFrame = pTmpFrame->FindMaster(); + } + pMasterObjAnchorFrame = pTmpFrame; + } + + if ( pMasterObjAnchorFrame && pMasterObjAnchorFrame == pProposedAnchorFrame ) + { + pRetDrawObj = GetMaster(); + } + else + { + const auto ppFoundVirtObj(std::find_if(maDrawVirtObjs.begin(), maDrawVirtObjs.end(), + VirtObjAnchoredAtFramePred(pProposedAnchorFrame))); + if(ppFoundVirtObj != maDrawVirtObjs.end()) + pRetDrawObj = ppFoundVirtObj->get(); + } + + return pRetDrawObj; +} + +void SwDrawContact::NotifyBackgroundOfAllVirtObjs(const tools::Rectangle* pOldBoundRect) +{ + for(const auto& rpDrawVirtObj : maDrawVirtObjs) + { + SwDrawVirtObj* pDrawVirtObj(rpDrawVirtObj.get()); + if ( pDrawVirtObj->GetAnchorFrame() ) + { + // #i34640# - determine correct page frame + SwPageFrame* pPage = pDrawVirtObj->AnchoredObj().FindPageFrameOfAnchor(); + if( pOldBoundRect && pPage ) + { + SwRect aOldRect( *pOldBoundRect ); + aOldRect.Pos() += pDrawVirtObj->GetOffset(); + if( aOldRect.HasArea() ) + ::Notify_Background( pDrawVirtObj, pPage, + aOldRect, PrepareHint::FlyFrameLeave,true); + } + // #i34640# - include spacing for wrapping + SwRect aRect( pDrawVirtObj->GetAnchoredObj().GetObjRectWithSpaces() ); + if (aRect.HasArea() && pPage) + { + SwPageFrame* pPg = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( aRect, pPage ))); + if ( pPg ) + ::Notify_Background( pDrawVirtObj, pPg, aRect, + PrepareHint::FlyFrameArrive, true ); + } + ::ClrContourCache( pDrawVirtObj ); + } + } +} + +/// local method to notify the background for a drawing object - #i26791# +static void lcl_NotifyBackgroundOfObj( SwDrawContact const & _rDrawContact, + const SdrObject& _rObj, + const tools::Rectangle* _pOldObjRect ) +{ + // #i34640# + SwAnchoredObject* pAnchoredObj = + const_cast<SwAnchoredObject*>(_rDrawContact.GetAnchoredObj( &_rObj )); + if ( !(pAnchoredObj && pAnchoredObj->GetAnchorFrame()) ) + return; + + // #i34640# - determine correct page frame + SwPageFrame* pPageFrame = pAnchoredObj->FindPageFrameOfAnchor(); + if( _pOldObjRect && pPageFrame ) + { + SwRect aOldRect( *_pOldObjRect ); + if( aOldRect.HasArea() ) + { + // #i34640# - determine correct page frame + SwPageFrame* pOldPageFrame = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( aOldRect, pPageFrame ))); + ::Notify_Background( &_rObj, pOldPageFrame, aOldRect, + PrepareHint::FlyFrameLeave, true); + } + } + // #i34640# - include spacing for wrapping + SwRect aNewRect( pAnchoredObj->GetObjRectWithSpaces() ); + if( aNewRect.HasArea() && pPageFrame ) + { + pPageFrame = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( aNewRect, pPageFrame ))); + ::Notify_Background( &_rObj, pPageFrame, aNewRect, + PrepareHint::FlyFrameArrive, true ); + } + ClrContourCache( &_rObj ); +} + +void SwDrawContact::Changed( const SdrObject& rObj, + SdrUserCallType eType, + const tools::Rectangle& rOldBoundRect ) +{ + // #i26791# - no event handling, if existing <SwViewShell> + // is in construction + SwDoc* pDoc = GetFormat()->GetDoc(); + if ( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && + pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->IsInConstructor() ) + { + return; + } + + // #i44339# + // no event handling, if document is in destruction. + // Exception: It's the SdrUserCallType::Delete event + if ( pDoc->IsInDtor() && eType != SdrUserCallType::Delete ) + { + return; + } + + //Put on Action, but not if presently anywhere an action runs. + bool bHasActions(true); + SwRootFrame *pTmpRoot = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + if ( pTmpRoot && pTmpRoot->IsCallbackActionEnabled() ) + { + SwViewShell* const pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + if ( pSh ) + { + for(SwViewShell& rShell : pSh->GetRingContainer() ) + { + if ( rShell.Imp()->IsAction() || rShell.Imp()->IsIdleAction() ) + { + bHasActions = true; + break; + } + bHasActions = false; + } + } + if(!bHasActions) + pTmpRoot->StartAllAction(); + } + SdrObjUserCall::Changed( rObj, eType, rOldBoundRect ); + Changed_( rObj, eType, &rOldBoundRect ); //Attention, possibly suicidal! + + if(!bHasActions) + pTmpRoot->EndAllAction(); +} + +/// helper class for method <SwDrawContact::Changed_(..)> for handling nested +/// <SdrObjUserCall> events +class NestedUserCallHdl +{ + private: + SwDrawContact* mpDrawContact; + bool mbParentUserCallActive; + SdrUserCallType meParentUserCallEventType; + + public: + NestedUserCallHdl( SwDrawContact* _pDrawContact, + SdrUserCallType _eEventType ) + : mpDrawContact( _pDrawContact ), + mbParentUserCallActive( _pDrawContact->mbUserCallActive ), + meParentUserCallEventType( _pDrawContact->meEventTypeOfCurrentUserCall ) + { + mpDrawContact->mbUserCallActive = true; + mpDrawContact->meEventTypeOfCurrentUserCall = _eEventType; + } + + ~NestedUserCallHdl() + { + if ( mpDrawContact ) + { + mpDrawContact->mbUserCallActive = mbParentUserCallActive; + mpDrawContact->meEventTypeOfCurrentUserCall = meParentUserCallEventType; + } + } + + void DrawContactDeleted() + { + mpDrawContact = nullptr; + } + + bool IsNestedUserCall() const + { + return mbParentUserCallActive; + } + + void AssertNestedUserCall() + { + if ( !IsNestedUserCall() ) + return; + + bool bTmpAssert( true ); + // Currently its known, that a nested event SdrUserCallType::Resize + // could occur during parent user call SdrUserCallType::Inserted, + // SdrUserCallType::Delete and SdrUserCallType::Resize for edge objects. + // Also possible are nested SdrUserCallType::ChildResize events for + // edge objects + // Thus, assert all other combinations + if ( ( meParentUserCallEventType == SdrUserCallType::Inserted || + meParentUserCallEventType == SdrUserCallType::Delete || + meParentUserCallEventType == SdrUserCallType::Resize ) && + mpDrawContact->meEventTypeOfCurrentUserCall == SdrUserCallType::Resize ) + { + bTmpAssert = false; + } + else if ( meParentUserCallEventType == SdrUserCallType::ChildResize && + mpDrawContact->meEventTypeOfCurrentUserCall == SdrUserCallType::ChildResize ) + { + bTmpAssert = false; + } + + if ( bTmpAssert ) + { + OSL_FAIL( "<SwDrawContact::Changed_(..)> - unknown nested <UserCall> event. This is serious." ); + } + } +}; + +/// Notify the format's textbox that it should reconsider its position / size. +static void lcl_textBoxSizeNotify(SwFrameFormat* pFormat) +{ + if (SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT)) + { + // Just notify the textbox that the size has changed, the actual object size is not interesting. + SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE> aResizeSet(pFormat->GetDoc()->GetAttrPool()); + SwFormatFrameSize aSize; + aResizeSet.Put(aSize); + SwTextBoxHelper::syncFlyFrameAttr(*pFormat, aResizeSet, pFormat->FindRealSdrObject()); + } +} + +// !!!ATTENTION!!! The object may commit suicide!!! + +void SwDrawContact::Changed_( const SdrObject& rObj, + SdrUserCallType eType, + const tools::Rectangle* pOldBoundRect ) +{ + // suppress handling of nested <SdrObjUserCall> events + NestedUserCallHdl aNestedUserCallHdl( this, eType ); + if ( aNestedUserCallHdl.IsNestedUserCall() ) + { + aNestedUserCallHdl.AssertNestedUserCall(); + return; + } + // do *not* notify, if document is destructing + // #i35912# - do *not* notify for as-character anchored + // drawing objects. + // #i35007# + // improvement: determine as-character anchored object flag only once. + const bool bAnchoredAsChar = ObjAnchoredAsChar(); + const bool bNotify = !(GetFormat()->GetDoc()->IsInDtor()) && + ( css::text::WrapTextMode_THROUGH != GetFormat()->GetSurround().GetSurround() ) && + !bAnchoredAsChar; + switch( eType ) + { + case SdrUserCallType::Delete: + { + if ( bNotify ) + { + lcl_NotifyBackgroundOfObj( *this, rObj, pOldBoundRect ); + // --> #i36181# - background of 'virtual' + // drawing objects have also been notified. + NotifyBackgroundOfAllVirtObjs( pOldBoundRect ); + } + DisconnectFromLayout( false ); + mbMasterObjCleared = true; + delete this; + // --> #i65784# Prevent memory corruption + aNestedUserCallHdl.DrawContactDeleted(); + break; + } + case SdrUserCallType::Inserted: + { + if ( mbDisconnectInProgress ) + { + OSL_FAIL( "<SwDrawContact::Changed_(..)> - Insert event during disconnection from layout is invalid." ); + } + else + { + ConnectToLayout(); + if ( bNotify ) + { + lcl_NotifyBackgroundOfObj( *this, rObj, pOldBoundRect ); + } + } + break; + } + case SdrUserCallType::Removed: + { + if ( bNotify ) + { + lcl_NotifyBackgroundOfObj( *this, rObj, pOldBoundRect ); + } + DisconnectFromLayout( false ); + break; + } + case SdrUserCallType::ChildInserted : + case SdrUserCallType::ChildRemoved : + { + // --> #i113730# + // force layer of controls for group objects containing control objects + if(dynamic_cast< SdrObjGroup* >(maAnchoredDrawObj.DrawObj())) + { + if(::CheckControlLayer(maAnchoredDrawObj.DrawObj())) + { + const IDocumentDrawModelAccess& rIDDMA = static_cast<SwFrameFormat*>(GetRegisteredInNonConst())->getIDocumentDrawModelAccess(); + const SdrLayerID aCurrentLayer(maAnchoredDrawObj.DrawObj()->GetLayer()); + const SdrLayerID aControlLayerID(rIDDMA.GetControlsId()); + const SdrLayerID aInvisibleControlLayerID(rIDDMA.GetInvisibleControlsId()); + + if(aCurrentLayer != aControlLayerID && aCurrentLayer != aInvisibleControlLayerID) + { + if ( aCurrentLayer == rIDDMA.GetInvisibleHellId() || + aCurrentLayer == rIDDMA.GetInvisibleHeavenId() ) + { + maAnchoredDrawObj.DrawObj()->SetLayer(aInvisibleControlLayerID); + } + else + { + maAnchoredDrawObj.DrawObj()->SetLayer(aControlLayerID); + } + } + } + } + [[fallthrough]]; + } + case SdrUserCallType::MoveOnly: + case SdrUserCallType::Resize: + case SdrUserCallType::ChildMoveOnly : + case SdrUserCallType::ChildResize : + case SdrUserCallType::ChildChangeAttr : + case SdrUserCallType::ChildDelete : + { + // #i31698# - improvement + // get instance <SwAnchoredDrawObject> only once + const SwAnchoredDrawObject* pAnchoredDrawObj = + static_cast<const SwAnchoredDrawObject*>( GetAnchoredObj( &rObj ) ); + + /* protect against NULL pointer dereferencing */ + if(!pAnchoredDrawObj) + { + break; + } + + // #i26791# - adjust positioning and alignment attributes, + // if positioning of drawing object isn't in progress. + // #i53320# - no adjust of positioning attributes, + // if drawing object isn't positioned. + if ( !pAnchoredDrawObj->IsPositioningInProgress() && + !pAnchoredDrawObj->NotYetPositioned() ) + { + // #i34748# - If no last object rectangle is + // provided by the anchored object, use parameter <pOldBoundRect>. + const tools::Rectangle& aOldObjRect = pAnchoredDrawObj->GetLastObjRect() + ? *(pAnchoredDrawObj->GetLastObjRect()) + : *pOldBoundRect; + // #i79400# + // always invalidate object rectangle inclusive spaces + pAnchoredDrawObj->InvalidateObjRectWithSpaces(); + // #i41324# - notify background before + // adjusting position + if ( bNotify ) + { + // #i31573# - correction + // background of given drawing object. + lcl_NotifyBackgroundOfObj( *this, rObj, &aOldObjRect ); + } + // #i31698# - determine layout direction + // via draw frame format. + SwFrameFormat::tLayoutDir eLayoutDir = + pAnchoredDrawObj->GetFrameFormat().GetLayoutDir(); + // use geometry of drawing object + tools::Rectangle aObjRect( rObj.GetSnapRect() ); + // If drawing object is a member of a group, the adjustment + // of the positioning and the alignment attributes has to + // be done for the top group object. + if ( rObj.getParentSdrObjectFromSdrObject() ) + { + const SdrObject* pGroupObj = rObj.getParentSdrObjectFromSdrObject(); + while ( pGroupObj->getParentSdrObjectFromSdrObject() ) + { + pGroupObj = pGroupObj->getParentSdrObjectFromSdrObject(); + } + // use geometry of drawing object + aObjRect = pGroupObj->GetSnapRect(); + + SwTextBoxHelper::synchronizeGroupTextBoxProperty(&SwTextBoxHelper::changeAnchor, GetFormat(), &const_cast<SdrObject&>(rObj)); + SwTextBoxHelper::synchronizeGroupTextBoxProperty(&SwTextBoxHelper::syncTextBoxSize, GetFormat(), &const_cast<SdrObject&>(rObj)); + + } + SwTwips nXPosDiff(0); + SwTwips nYPosDiff(0); + switch ( eLayoutDir ) + { + case SwFrameFormat::HORI_L2R: + { + nXPosDiff = aObjRect.Left() - aOldObjRect.Left(); + nYPosDiff = aObjRect.Top() - aOldObjRect.Top(); + } + break; + case SwFrameFormat::HORI_R2L: + { + nXPosDiff = aOldObjRect.Right() - aObjRect.Right(); + nYPosDiff = aObjRect.Top() - aOldObjRect.Top(); + } + break; + case SwFrameFormat::VERT_R2L: + { + nXPosDiff = aObjRect.Top() - aOldObjRect.Top(); + nYPosDiff = aOldObjRect.Right() - aObjRect.Right(); + } + break; + default: + { + assert(!"<SwDrawContact::Changed_(..)> - unsupported layout direction"); + } + } + SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT> aSet( GetFormat()->GetDoc()->GetAttrPool() ); + const SwFormatVertOrient& rVert = GetFormat()->GetVertOrient(); + if ( nYPosDiff != 0 ) + { + if ( rVert.GetRelationOrient() == text::RelOrientation::CHAR || + rVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE ) + { + nYPosDiff = -nYPosDiff; + } + aSet.Put( SwFormatVertOrient( rVert.GetPos()+nYPosDiff, + text::VertOrientation::NONE, + rVert.GetRelationOrient() ) ); + } + + const SwFormatHoriOrient& rHori = GetFormat()->GetHoriOrient(); + if ( !bAnchoredAsChar && nXPosDiff != 0 ) + { + aSet.Put( SwFormatHoriOrient( rHori.GetPos()+nXPosDiff, + text::HoriOrientation::NONE, + rHori.GetRelationOrient() ) ); + } + + if ( nYPosDiff || + ( !bAnchoredAsChar && nXPosDiff != 0 ) ) + { + GetFormat()->GetDoc()->SetFlyFrameAttr( *(GetFormat()), aSet ); + // keep new object rectangle, to avoid multiple + // changes of the attributes by multiple event from + // the drawing layer - e.g. group objects and its members + // #i34748# - use new method + // <SwAnchoredDrawObject::SetLastObjRect(..)>. + const_cast<SwAnchoredDrawObject*>(pAnchoredDrawObj) + ->SetLastObjRect( aObjRect ); + } + else if ( aObjRect.GetSize() != aOldObjRect.GetSize() ) + { + InvalidateObjs_(); + // #i35007# - notify anchor frame + // of as-character anchored object + if ( bAnchoredAsChar ) + { + SwFrame* pAnchorFrame = const_cast<SwAnchoredDrawObject*>(pAnchoredDrawObj)->AnchorFrame(); + if(pAnchorFrame) + { + pAnchorFrame->Prepare( PrepareHint::FlyFrameAttributesChanged, GetFormat() ); + } + } + + lcl_textBoxSizeNotify(GetFormat()); + } + else if (eType == SdrUserCallType::Resize) + // Even if the bounding box of the shape didn't change, + // notify about the size change, as an adjustment change + // may affect the size of the underlying textbox. + lcl_textBoxSizeNotify(GetFormat()); + } + + // tdf#135198: keep text box together with its shape + const SwPageFrame* rPageFrame = pAnchoredDrawObj->GetPageFrame(); + if (rPageFrame && rPageFrame->isFrameAreaPositionValid() && GetFormat() + && GetFormat()->GetOtherTextBoxFormats()) + { + SwDoc* const pDoc = GetFormat()->GetDoc(); + + // avoid Undo creation + ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); + + // hide any artificial "changes" made by synchronizing the textbox position + const bool bEnableSetModified = pDoc->getIDocumentState().IsEnableSetModified(); + pDoc->getIDocumentState().SetEnableSetModified(false); + + SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT, RES_ANCHOR, RES_ANCHOR> + aSyncSet( pDoc->GetAttrPool() ); + aSyncSet.Put(GetFormat()->GetHoriOrient()); + bool bRelToTableCell(false); + aSyncSet.Put(SwFormatVertOrient(pAnchoredDrawObj->GetRelPosToPageFrame(false, bRelToTableCell).getY(), + text::VertOrientation::NONE, + text::RelOrientation::PAGE_FRAME)); + aSyncSet.Put(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, rPageFrame->GetPhyPageNum())); + + auto pSdrObj = const_cast<SdrObject*>(&rObj); + if (pSdrObj != GetFormat()->FindRealSdrObject()) + { + SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE> aSet( pDoc->GetAttrPool() ); + + aSet.Put(aSyncSet); + aSet.Put(pSdrObj->GetMergedItem(RES_FRM_SIZE)); + SwTextBoxHelper::syncFlyFrameAttr(*GetFormat(), aSet, pSdrObj); + + SwTextBoxHelper::synchronizeGroupTextBoxProperty( + &SwTextBoxHelper::changeAnchor, GetFormat(), + GetFormat()->FindRealSdrObject()); + SwTextBoxHelper::synchronizeGroupTextBoxProperty( + &SwTextBoxHelper::syncTextBoxSize, GetFormat(), + GetFormat()->FindRealSdrObject()); + } + else + SwTextBoxHelper::syncFlyFrameAttr(*GetFormat(), aSyncSet, GetFormat()->FindRealSdrObject()); + + pDoc->getIDocumentState().SetEnableSetModified(bEnableSetModified); + } + } + break; + case SdrUserCallType::ChangeAttr: + if ( bNotify ) + { + lcl_NotifyBackgroundOfObj( *this, rObj, pOldBoundRect ); + } + break; + default: + break; + } +} + +namespace +{ + const SwFormatAnchor* lcl_getAnchorFormat( const SfxPoolItem& _rItem ) + { + sal_uInt16 nWhich = _rItem.Which(); + const SwFormatAnchor* pAnchorFormat = nullptr; + if ( RES_ATTRSET_CHG == nWhich ) + { + pAnchorFormat = static_cast<const SwAttrSetChg&>(_rItem).GetChgSet()-> + GetItemIfSet( RES_ANCHOR, false ); + } + else if ( RES_ANCHOR == nWhich ) + { + pAnchorFormat = &static_cast<const SwFormatAnchor&>(_rItem); + } + return pAnchorFormat; + } +} + +void SwDrawContact::SwClientNotify(const SwModify& rMod, const SfxHint& rHint) +{ + SwClient::SwClientNotify(rMod, rHint); // needed as SwContact::SwClientNotify doesn't explicitly call SwClient::SwClientNotify + SwContact::SwClientNotify(rMod, rHint); + if (rHint.GetId() == SfxHintId::SwLegacyModify) + { + auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint); + SAL_WARN_IF(mbDisconnectInProgress, "sw.core", "<SwDrawContact::Modify(..)> called during disconnection."); + + const SfxPoolItem* pNew = pLegacyHint->m_pNew; + sal_uInt16 nWhich = pNew ? pNew->Which() : 0; + if(const SwFormatAnchor* pNewAnchorFormat = pNew ? lcl_getAnchorFormat(*pNew) : nullptr) + { + // Do not respond to a Reset Anchor! + if(GetFormat()->GetAttrSet().GetItemState(RES_ANCHOR, false) == SfxItemState::SET) + { + // no connect to layout during disconnection + if(!mbDisconnectInProgress) + { + // determine old object rectangle of 'master' drawing object + // for notification + const tools::Rectangle* pOldRect = nullptr; + tools::Rectangle aOldRect; + if(GetAnchorFrame()) + { + // --> #i36181# - include spacing in object + // rectangle for notification. + aOldRect = maAnchoredDrawObj.GetObjRectWithSpaces().SVRect(); + pOldRect = &aOldRect; + } + // re-connect to layout due to anchor format change + ConnectToLayout(pNewAnchorFormat); + // notify background of drawing objects + lcl_NotifyBackgroundOfObj(*this, *GetMaster(), pOldRect); + NotifyBackgroundOfAllVirtObjs(pOldRect); + + const SwFormatAnchor* pOldAnchorFormat = pLegacyHint->m_pOld ? lcl_getAnchorFormat(*pLegacyHint->m_pOld) : nullptr; + if(!pOldAnchorFormat || (pOldAnchorFormat->GetAnchorId() != pNewAnchorFormat->GetAnchorId())) + { + if(maAnchoredDrawObj.DrawObj()) + { + // --> #i102752# + // assure that a ShapePropertyChangeNotifier exists + maAnchoredDrawObj.DrawObj()->notifyShapePropertyChange(svx::ShapePropertyProviderId::TextDocAnchor); + } + else + SAL_WARN("sw.core", "SwDrawContact::Modify: no draw object here?"); + } + } + } + else + DisconnectFromLayout(); + } + else if (nWhich == RES_REMOVE_UNO_OBJECT) + {} // nothing to do + // --> #i62875# - no further notification, if not connected to Writer layout + else if ( maAnchoredDrawObj.GetAnchorFrame() && + maAnchoredDrawObj.GetDrawObj()->GetUserCall() ) + { + bool bUpdateSortedObjsList(false); + switch(nWhich) + { + case RES_UL_SPACE: + case RES_LR_SPACE: + case RES_HORI_ORIENT: + case RES_VERT_ORIENT: + case RES_FOLLOW_TEXT_FLOW: // #i28701# - add attribute 'Follow text flow' + break; + case RES_SURROUND: + case RES_OPAQUE: + case RES_WRAP_INFLUENCE_ON_OBJPOS: + // --> #i28701# - on change of wrapping style, hell|heaven layer, + // or wrapping style influence an update of the <SwSortedObjs> list, + // the drawing object is registered in, has to be performed. This is triggered + // by the 1st parameter of method call <InvalidateObjs_(..)>. + bUpdateSortedObjsList = true; + break; + case RES_ATTRSET_CHG: // #i35443# + { + auto pChgSet = static_cast<const SwAttrSetChg*>(pNew)->GetChgSet(); + if(pChgSet->GetItemState(RES_SURROUND, false) == SfxItemState::SET || + pChgSet->GetItemState(RES_OPAQUE, false) == SfxItemState::SET || + pChgSet->GetItemState(RES_WRAP_INFLUENCE_ON_OBJPOS, false) == SfxItemState::SET) + bUpdateSortedObjsList = true; + } + break; + default: + assert(!"<SwDraw Contact::Modify(..)> - unhandled attribute?"); + } + lcl_NotifyBackgroundOfObj(*this, *GetMaster(), nullptr); + NotifyBackgroundOfAllVirtObjs(nullptr); + InvalidateObjs_(bUpdateSortedObjsList); + } + + // #i51474# + GetAnchoredObj(nullptr)->ResetLayoutProcessBools(); + } + else if (auto pDrawFrameFormatHint = dynamic_cast<const sw::DrawFrameFormatHint*>(&rHint)) + { + switch(pDrawFrameFormatHint->m_eId) + { + case sw::DrawFrameFormatHintId::DYING: + delete this; + break; + case sw::DrawFrameFormatHintId::PREPPASTING: + MoveObjToVisibleLayer(GetMaster()); + break; + case sw::DrawFrameFormatHintId::PREP_INSERT_FLY: + InsertMasterIntoDrawPage(); + // #i40845# - follow-up of #i35635# + // move object to visible layer + MoveObjToVisibleLayer(GetMaster()); + // tdf#135661 InsertMasterIntoDrawPage may have created a new + // SwXShape with null m_pFormat; fix that + SwXShape::AddExistingShapeToFormat(*GetMaster()); + break; + case sw::DrawFrameFormatHintId::PREP_DELETE_FLY: + RemoveMasterFromDrawPage(); + break; + case sw::DrawFrameFormatHintId::PAGE_OUT_OF_BOUNDS: + case sw::DrawFrameFormatHintId::DELETE_FRAMES: + DisconnectFromLayout(); + break; + case sw::DrawFrameFormatHintId::MAKE_FRAMES: + ConnectToLayout(); + break; + case sw::DrawFrameFormatHintId::POST_RESTORE_FLY_ANCHOR: + GetAnchoredObj(GetMaster())->MakeObjPos(); + break; + default: + ; + } + } + else if (auto pCheckDrawFrameFormatLayerHint = dynamic_cast<const sw::CheckDrawFrameFormatLayerHint*>(&rHint)) + { + *(pCheckDrawFrameFormatLayerHint->m_bCheckControlLayer) |= (GetMaster() && CheckControlLayer(GetMaster())); + } + else if (auto pContactChangedHint = dynamic_cast<const sw::ContactChangedHint*>(&rHint)) + { + if(!*pContactChangedHint->m_ppObject) + *pContactChangedHint->m_ppObject = GetMaster(); + auto pObject = *pContactChangedHint->m_ppObject; + Changed(*pObject, SdrUserCallType::Delete, pObject->GetLastBoundRect()); + } + else if (auto pDrawFormatLayoutCopyHint = dynamic_cast<const sw::DrawFormatLayoutCopyHint*>(&rHint)) + { + const SwDrawFrameFormat& rFormat = static_cast<const SwDrawFrameFormat&>(rMod); + new SwDrawContact( + &pDrawFormatLayoutCopyHint->m_rDestFormat, + pDrawFormatLayoutCopyHint->m_rDestDoc.CloneSdrObj( + *GetMaster(), + pDrawFormatLayoutCopyHint->m_rDestDoc.IsCopyIsMove() && &pDrawFormatLayoutCopyHint->m_rDestDoc == rFormat.GetDoc())); + // #i49730# - notify draw frame format that position attributes are + // already set, if the position attributes are already set at the + // source draw frame format. + if(rFormat.IsPosAttrSet()) + pDrawFormatLayoutCopyHint->m_rDestFormat.PosAttrSet(); + } + else if (auto pRestoreFlyAnchorHint = dynamic_cast<const sw::RestoreFlyAnchorHint*>(&rHint)) + { + SdrObject* pObj = GetMaster(); + if(GetAnchorFrame() && !pObj->IsInserted()) + { + auto pDrawModel = const_cast<SwDrawFrameFormat&>(static_cast<const SwDrawFrameFormat&>(rMod)).GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + assert(pDrawModel); + pDrawModel->GetPage(0)->InsertObject(pObj); + } + pObj->SetRelativePos(pRestoreFlyAnchorHint->m_aPos); + } + else if (auto pCreatePortionHint = dynamic_cast<const sw::CreatePortionHint*>(&rHint)) + { + if(*pCreatePortionHint->m_ppContact) + return; + *pCreatePortionHint->m_ppContact = this; // This is kind of ridiculous: the FrameFormat doesn't even hold a pointer to the contact itself, but here we are leaking it out randomly + if(!GetAnchorFrame()) + { + // No direct positioning needed any more + ConnectToLayout(); + // Move object to visible layer + MoveObjToVisibleLayer(GetMaster()); + } + } + else if (auto pCollectTextObjectsHint = dynamic_cast<const sw::CollectTextObjectsHint*>(&rHint)) + { + auto pSdrO = GetMaster(); + if(!pSdrO) + return; + if(dynamic_cast<const SdrObjGroup*>(pSdrO)) + { + SdrObjListIter aListIter(*pSdrO, SdrIterMode::DeepNoGroups); + //iterate inside of a grouped object + while(aListIter.IsMore()) + { + SdrObject* pSdrOElement = aListIter.Next(); + auto pTextObj = const_cast<SdrTextObj*>(dynamic_cast<const SdrTextObj*>(pSdrOElement)); + if(pTextObj && pTextObj->HasText()) + pCollectTextObjectsHint->m_rTextObjects.push_back(pTextObj); + } + } + else if(auto pTextObj = const_cast<SdrTextObj*>(dynamic_cast<const SdrTextObj*>(pSdrO))) + { + if(pTextObj->HasText()) + pCollectTextObjectsHint->m_rTextObjects.push_back(pTextObj); + } + } + else if (auto pGetZOrdnerHint = dynamic_cast<const sw::GetZOrderHint*>(&rHint)) + { + auto pFormat(dynamic_cast<const SwFrameFormat*>(&rMod)); + if(pFormat->Which() == RES_DRAWFRMFMT) + pGetZOrdnerHint->m_rnZOrder = GetMaster()->GetOrdNum(); + } + else if (auto pConnectedHint = dynamic_cast<const sw::GetObjectConnectedHint*>(&rHint)) + { + pConnectedHint->m_risConnected |= (GetAnchorFrame() != nullptr); + } +} + +// #i26791# +// #i28701# - added parameter <_bUpdateSortedObjsList> +void SwDrawContact::InvalidateObjs_( const bool _bUpdateSortedObjsList ) +{ + for(const auto& rpDrawVirtObj : maDrawVirtObjs) + // invalidate position of existing 'virtual' drawing objects + { + SwDrawVirtObj* pDrawVirtObj(rpDrawVirtObj.get()); + // #i33313# - invalidation only for connected + // 'virtual' drawing objects + if ( pDrawVirtObj->IsConnected() ) + { + pDrawVirtObj->AnchoredObj().InvalidateObjPos(); + // #i28701# + if ( _bUpdateSortedObjsList ) + { + pDrawVirtObj->AnchoredObj().UpdateObjInSortedList(); + } + } + } + + // invalidate position of 'master' drawing object + SwAnchoredObject* pAnchoredObj = GetAnchoredObj( nullptr ); + pAnchoredObj->InvalidateObjPos(); + // #i28701# + if ( _bUpdateSortedObjsList ) + { + pAnchoredObj->UpdateObjInSortedList(); + } +} + +void SwDrawContact::DisconnectFromLayout( bool _bMoveMasterToInvisibleLayer ) +{ + mbDisconnectInProgress = true; + + // --> #i36181# - notify background of drawing object + if ( _bMoveMasterToInvisibleLayer && + !(GetFormat()->GetDoc()->IsInDtor()) && + GetAnchorFrame() && !GetAnchorFrame()->IsInDtor() ) + { + const tools::Rectangle aOldRect( maAnchoredDrawObj.GetObjRectWithSpaces().SVRect() ); + lcl_NotifyBackgroundOfObj( *this, *GetMaster(), &aOldRect ); + NotifyBackgroundOfAllVirtObjs( &aOldRect ); + } + + // remove 'virtual' drawing objects from writer + // layout and from drawing page + for(auto& rpVirtDrawObj : maDrawVirtObjs) + { + rpVirtDrawObj->RemoveFromWriterLayout(); + rpVirtDrawObj->RemoveFromDrawingPage(); + } + + if ( maAnchoredDrawObj.GetAnchorFrame() ) + { + maAnchoredDrawObj.AnchorFrame()->RemoveDrawObj( maAnchoredDrawObj ); + } + + if ( _bMoveMasterToInvisibleLayer && GetMaster() && GetMaster()->IsInserted() ) + { + SdrViewIter aIter( GetMaster() ); + for( SdrView* pView = aIter.FirstView(); pView; + pView = aIter.NextView() ) + { + pView->MarkObj( GetMaster(), pView->GetSdrPageView(), true ); + } + + // Instead of removing 'master' object from drawing page, move the + // 'master' drawing object into the corresponding invisible layer. + { + //static_cast<SwFrameFormat*>(GetRegisteredIn())->getIDocumentDrawModelAccess()->GetDrawModel()->GetPage(0)-> + // RemoveObject( GetMaster()->GetOrdNum() ); + // #i18447# - in order to consider group object correct + // use new method <SwDrawContact::MoveObjToInvisibleLayer(..)> + MoveObjToInvisibleLayer( GetMaster() ); + } + } + + mbDisconnectInProgress = false; +} + +/// method to remove 'master' drawing object from drawing page. +void SwDrawContact::RemoveMasterFromDrawPage() +{ + if ( GetMaster() ) + { + GetMaster()->SetUserCall( nullptr ); + if ( GetMaster()->IsInserted() ) + { + static_cast<SwFrameFormat*>(GetRegisteredIn())->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0)-> + RemoveObject( GetMaster()->GetOrdNum() ); + } + } +} + +// disconnect for a dedicated drawing object - could be 'master' or 'virtual'. +// a 'master' drawing object will disconnect a 'virtual' drawing object +// in order to take its place. +// #i19919# - no special case, if drawing object isn't in +// page header/footer, in order to get drawing objects in repeating table headers +// also working. +void SwDrawContact::DisconnectObjFromLayout( SdrObject* _pDrawObj ) +{ + if ( auto pSwDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( _pDrawObj) ) + { + pSwDrawVirtObj->RemoveFromWriterLayout(); + pSwDrawVirtObj->RemoveFromDrawingPage(); + } + else + { + const auto ppVirtDrawObj(std::find_if(maDrawVirtObjs.begin(), maDrawVirtObjs.end(), + [] (const SwDrawVirtObjPtr& pObj) { return pObj->IsConnected(); })); + + if(ppVirtDrawObj != maDrawVirtObjs.end()) + { + // replace found 'virtual' drawing object by 'master' drawing + // object and disconnect the 'virtual' one + SwDrawVirtObj* pDrawVirtObj(ppVirtDrawObj->get()); + SwFrame* pNewAnchorFrameOfMaster = pDrawVirtObj->AnchorFrame(); + // disconnect 'virtual' drawing object + pDrawVirtObj->RemoveFromWriterLayout(); + pDrawVirtObj->RemoveFromDrawingPage(); + // disconnect 'master' drawing object from current frame + GetAnchorFrame()->RemoveDrawObj( maAnchoredDrawObj ); + // re-connect 'master' drawing object to frame of found 'virtual' + // drawing object. + pNewAnchorFrameOfMaster->AppendDrawObj( maAnchoredDrawObj ); + } + else + { + // no connected 'virtual' drawing object found. Thus, disconnect + // completely from layout. + DisconnectFromLayout(); + } + } +} + +static SwTextFrame* lcl_GetFlyInContentAnchor( SwTextFrame* _pProposedAnchorFrame, + SwPosition const& rAnchorPos) +{ + SwTextFrame* pAct = _pProposedAnchorFrame; + SwTextFrame* pTmp; + TextFrameIndex const nTextOffset(_pProposedAnchorFrame->MapModelToViewPos(rAnchorPos)); + do + { + pTmp = pAct; + pAct = pTmp->GetFollow(); + } + while (pAct && nTextOffset >= pAct->GetOffset()); + return pTmp; +} + +void SwDrawContact::ConnectToLayout( const SwFormatAnchor* pAnch ) +{ + // *no* connect to layout during disconnection from layout. + if ( mbDisconnectInProgress ) + { + OSL_FAIL( "<SwDrawContact::ConnectToLayout(..)> called during disconnection."); + return; + } + + // --> #i33909# - *no* connect to layout, if 'master' drawing + // object isn't inserted in the drawing page + if ( !GetMaster()->IsInserted() ) + { + OSL_FAIL( "<SwDrawContact::ConnectToLayout(..)> - master drawing object not inserted -> no connect to layout. Please inform od@openoffice.org" ); + return; + } + + SwFrameFormat* pDrawFrameFormat = static_cast<SwFrameFormat*>(GetRegisteredIn()); + + if( !pDrawFrameFormat->getIDocumentLayoutAccess().GetCurrentViewShell() ) + return; + + // remove 'virtual' drawing objects from writer + // layout and from drawing page, and remove 'master' drawing object from + // writer layout - 'master' object will remain in drawing page. + DisconnectFromLayout( false ); + + if ( !pAnch ) + { + pAnch = &(pDrawFrameFormat->GetAnchor()); + } + + switch ( pAnch->GetAnchorId() ) + { + case RndStdIds::FLY_AT_PAGE: + { + sal_uInt16 nPgNum = pAnch->GetPageNum(); + SwViewShell *pShell = pDrawFrameFormat->getIDocumentLayoutAccess().GetCurrentViewShell(); + if( !pShell ) + break; + SwRootFrame* pRoot = pShell->GetLayout(); + SwPageFrame *pPage = static_cast<SwPageFrame*>(pRoot->Lower()); + + for ( sal_uInt16 i = 1; i < nPgNum && pPage; ++i ) + { + pPage = static_cast<SwPageFrame*>(pPage->GetNext()); + } + + if ( pPage ) + { + pPage->AppendDrawObj( maAnchoredDrawObj ); + } + else + //Looks stupid but is allowed (compare SwFEShell::SetPageObjsNewPage) + pRoot->SetAssertFlyPages(); + } + break; + + case RndStdIds::FLY_AT_CHAR: + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_FLY: + case RndStdIds::FLY_AS_CHAR: + { + if ( pAnch->GetAnchorId() == RndStdIds::FLY_AS_CHAR ) + { + ClrContourCache( GetMaster() ); + } + // support drawing objects in header/footer, + // but not control objects: + // anchor at first found frame the 'master' object and + // at the following frames 'virtual' drawing objects. + // Note: method is similar to <SwFlyFrameFormat::MakeFrames(..)> + sw::BroadcastingModify *pModify = nullptr; + if( pAnch->GetContentAnchor() ) + { + if ( pAnch->GetAnchorId() == RndStdIds::FLY_AT_FLY ) + { + SwNodeIndex aIdx( pAnch->GetContentAnchor()->nNode ); + SwContentNode* pCNd = pDrawFrameFormat->GetDoc()->GetNodes().GoNext( &aIdx ); + if (SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*pCNd).First()) + pModify = pCNd; + else + { + const SwNodeIndex& rIdx = pAnch->GetContentAnchor()->nNode; + SwFrameFormats& rFormats = *(pDrawFrameFormat->GetDoc()->GetSpzFrameFormats()); + for( auto pFlyFormat : rFormats ) + { + if( pFlyFormat->GetContent().GetContentIdx() && + rIdx == *(pFlyFormat->GetContent().GetContentIdx()) ) + { + pModify = pFlyFormat; + break; + } + } + } + } + else + { + pModify = pAnch->GetContentAnchor()->nNode.GetNode().GetContentNode(); + } + } + + // #i29199# - It is possible, that + // the anchor doesn't exist - E.g., reordering the + // sub-documents in a master document. + // Note: The anchor will be inserted later. + if ( !pModify ) + { + // break to end of the current switch case. + break; + } + + SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(*pModify); + SwFrame* pAnchorFrameOfMaster = nullptr; + for( SwFrame *pFrame = aIter.First(); pFrame; pFrame = aIter.Next() ) + { + // append drawing object, if + // (1) proposed anchor frame isn't a follow and... + const bool bFollow = pFrame->IsContentFrame() && static_cast<SwContentFrame*>(pFrame)->IsFollow(); + if (bFollow) + continue; + + // (2) drawing object isn't a control object to be anchored + // in header/footer. + const bool bControlInHF = ::CheckControlLayer(GetMaster()) && pFrame->FindFooterOrHeader(); + // tdf#129542 but make an exception for control objects so they can get added to just the first frame, + // the Master Anchor Frame and not the others + if (bControlInHF && pAnchorFrameOfMaster) + continue; + + bool bAdd; + if (RndStdIds::FLY_AT_FLY == pAnch->GetAnchorId()) + bAdd = true; + else + { + assert(pFrame->IsTextFrame()); + bAdd = IsAnchoredObjShown(*static_cast<SwTextFrame*>(pFrame), *pAnch); + } + + if( bAdd ) + { + if ( RndStdIds::FLY_AT_FLY == pAnch->GetAnchorId() && !pFrame->IsFlyFrame() ) + { + pFrame = pFrame->FindFlyFrame(); + assert(pFrame); + } + + // find correct follow for as character anchored objects + if ((pAnch->GetAnchorId() == RndStdIds::FLY_AS_CHAR) && + pFrame->IsTextFrame() ) + { + pFrame = lcl_GetFlyInContentAnchor( + static_cast<SwTextFrame*>(pFrame), + *pAnch->GetContentAnchor()); + } + + if ( !pAnchorFrameOfMaster ) + { + // append 'master' drawing object + pAnchorFrameOfMaster = pFrame; + pFrame->AppendDrawObj( maAnchoredDrawObj ); + } + else + { + // append 'virtual' drawing object + SwDrawVirtObj* pDrawVirtObj = AddVirtObj(*pFrame); + if ( pAnch->GetAnchorId() == RndStdIds::FLY_AS_CHAR ) + { + ClrContourCache( pDrawVirtObj ); + } + pFrame->AppendDrawObj( pDrawVirtObj->AnchoredObj() ); + + pDrawVirtObj->ActionChanged(); + } + + if ( pAnch->GetAnchorId() == RndStdIds::FLY_AS_CHAR ) + { + pFrame->InvalidatePrt(); + } + } + } + } + break; + default: + assert(!"Unknown Anchor."); + break; + } + if ( GetAnchorFrame() ) + { + ::setContextWritingMode( maAnchoredDrawObj.DrawObj(), GetAnchorFrame() ); + // #i26791# - invalidate objects instead of direct positioning + InvalidateObjs_(); + } +} + +/// insert 'master' drawing object into drawing page +void SwDrawContact::InsertMasterIntoDrawPage() +{ + if ( !GetMaster()->IsInserted() ) + { + GetFormat()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0) + ->InsertObject( GetMaster(), GetMaster()->GetOrdNumDirect() ); + } + GetMaster()->SetUserCall( this ); +} + +SwPageFrame* SwDrawContact::FindPage( const SwRect &rRect ) +{ + // --> #i28701# - use method <GetPageFrame()> + SwPageFrame* pPg = GetPageFrame(); + if ( !pPg && GetAnchorFrame() ) + pPg = GetAnchorFrame()->FindPageFrame(); + if ( pPg ) + pPg = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( rRect, pPg ))); + return pPg; +} + +void SwDrawContact::ChkPage() +{ + if ( mbDisconnectInProgress ) + { + OSL_FAIL( "<SwDrawContact::ChkPage()> called during disconnection." ); + return; + } + + // --> #i28701# + SwPageFrame* pPg = ( maAnchoredDrawObj.GetAnchorFrame() && + maAnchoredDrawObj.GetAnchorFrame()->IsPageFrame() ) + ? GetPageFrame() + : FindPage( SwRect(GetMaster()->GetCurrentBoundRect()) ); + if ( GetPageFrame() == pPg ) + return; + + // if drawing object is anchor in header/footer a change of the page + // is a dramatic change. Thus, completely re-connect to the layout + if ( maAnchoredDrawObj.GetAnchorFrame() && + maAnchoredDrawObj.GetAnchorFrame()->FindFooterOrHeader() ) + { + ConnectToLayout(); + } + else + { + // --> #i28701# - use methods <GetPageFrame()> and <SetPageFrame> + if ( GetPageFrame() ) + GetPageFrame()->RemoveDrawObjFromPage( maAnchoredDrawObj ); + pPg->AppendDrawObjToPage( maAnchoredDrawObj ); + maAnchoredDrawObj.SetPageFrame( pPg ); + } +} + +// Important note: +// method is called by method <SwDPage::ReplaceObject(..)>, which called its +// corresponding superclass method <FmFormPage::ReplaceObject(..)>. +// Note: 'master' drawing object *has* to be connected to layout triggered +// by the caller of this, if method is called. +void SwDrawContact::ChangeMasterObject(SdrObject* pNewMaster) +{ + DisconnectFromLayout( false ); + // consider 'virtual' drawing objects + RemoveAllVirtObjs(); + + GetMaster()->SetUserCall( nullptr ); + if(pNewMaster) + maAnchoredDrawObj.SetDrawObj(*pNewMaster); + else + mbMasterObjCleared = true; + GetMaster()->SetUserCall( this ); + + InvalidateObjs_(); +} + +/// get data collection of anchored objects, handled by with contact +void SwDrawContact::GetAnchoredObjs(std::vector<SwAnchoredObject*>& o_rAnchoredObjs) const +{ + o_rAnchoredObjs.push_back(const_cast<SwAnchoredDrawObject*>(&maAnchoredDrawObj)); + + for(auto& rpDrawVirtObj : maDrawVirtObjs) + o_rAnchoredObjs.push_back(&rpDrawVirtObj->AnchoredObj()); +} + +// AW: own sdr::contact::ViewContact (VC) sdr::contact::ViewObjectContact (VOC) needed +// since offset is defined different from SdrVirtObj's sdr::contact::ViewContactOfVirtObj. +// For paint, that offset is used by setting at the OutputDevice; for primitives this is +// not possible since we have no OutputDevice, but define the geometry itself. + +namespace sdr::contact +{ + namespace { + + class VOCOfDrawVirtObj : public ViewObjectContactOfSdrObj + { + protected: + /** + * This method is responsible for creating the graphical visualisation data which is + * stored/cached in the local primitive. Default gets view-independent Primitive from + * the ViewContact using ViewContact::getViewIndependentPrimitive2DContainer(), takes + * care of visibility, handles glue and ghosted. + * + * This method will not handle included hierarchies and not check geometric visibility. + */ + virtual void createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override; + + public: + VOCOfDrawVirtObj(ObjectContact& rObjectContact, ViewContact& rViewContact) + : ViewObjectContactOfSdrObj(rObjectContact, rViewContact) + { + } + }; + + class VCOfDrawVirtObj : public ViewContactOfVirtObj + { + protected: + /** Create an Object-Specific ViewObjectContact, set ViewContact and ObjectContact. + * + * Always needs to return something. Default is to create a standard ViewObjectContact + * containing the given ObjectContact and *this. + */ + virtual ViewObjectContact& CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) override; + + public: + /// basic constructor, used from SdrObject. + explicit VCOfDrawVirtObj(SwDrawVirtObj& rObj) + : ViewContactOfVirtObj(rObj) + { + } + + /// access to SwDrawVirtObj + SwDrawVirtObj& GetSwDrawVirtObj() const + { + return static_cast<SwDrawVirtObj&>(mrObject); + } + }; + + } +} // end of namespace sdr::contact + +namespace sdr::contact +{ + /// recursively collect primitive data from given VOC with given offset + static void impAddPrimitivesFromGroup(const ViewObjectContact& rVOC, const basegfx::B2DHomMatrix& rOffsetMatrix, const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DContainer& rxTarget) + { + const sal_uInt32 nSubHierarchyCount(rVOC.GetViewContact().GetObjectCount()); + + for(sal_uInt32 a(0); a < nSubHierarchyCount; a++) + { + const ViewObjectContact& rCandidate(rVOC.GetViewContact().GetViewContact(a).GetViewObjectContact(rVOC.GetObjectContact())); + + if(rCandidate.GetViewContact().GetObjectCount()) + { + // is a group object itself, call recursively + impAddPrimitivesFromGroup(rCandidate, rOffsetMatrix, rDisplayInfo, rxTarget); + } + else + { + // single object, add primitives; check model-view visibility + if(rCandidate.isPrimitiveVisible(rDisplayInfo)) + { + drawinglayer::primitive2d::Primitive2DContainer aNewSequence(rCandidate.getPrimitive2DSequence(rDisplayInfo)); + + if(!aNewSequence.empty()) + { + // get ranges + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(rCandidate.GetObjectContact().getViewInformation2D()); + const basegfx::B2DRange& aViewRange(rViewInformation2D.getViewport()); + basegfx::B2DRange aObjectRange(rCandidate.getObjectRange()); + + // correct with virtual object's offset + aObjectRange.transform(rOffsetMatrix); + + // check geometrical visibility (with offset) + if(!aViewRange.overlaps(aObjectRange)) + { + // not visible, release + aNewSequence.clear(); + } + } + + if(!aNewSequence.empty()) + { + rxTarget.append(aNewSequence); + } + } + } + } + } + + void VOCOfDrawVirtObj::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + // tdf#91260 have already checked top-level one is on the right page + assert(isPrimitiveVisible(rDisplayInfo)); + // nasty corner case: override to clear page frame to disable the + // sub-objects' anchor check, because their anchor is always on + // the first page that the page style is applied to + DisplayInfo aDisplayInfo(rDisplayInfo); + aDisplayInfo.SetWriterPageFrame(basegfx::B2IRectangle()); + const VCOfDrawVirtObj& rVC = static_cast< const VCOfDrawVirtObj& >(GetViewContact()); + const SdrObject& rReferencedObject = rVC.GetSwDrawVirtObj().GetReferencedObj(); + drawinglayer::primitive2d::Primitive2DContainer xRetval; + + // create offset transformation + basegfx::B2DHomMatrix aOffsetMatrix; + const Point aLocalOffset(rVC.GetSwDrawVirtObj().GetOffset()); + + if(aLocalOffset.X() || aLocalOffset.Y()) + { + aOffsetMatrix.set(0, 2, aLocalOffset.X()); + aOffsetMatrix.set(1, 2, aLocalOffset.Y()); + } + + if(dynamic_cast<const SdrObjGroup*>( &rReferencedObject) != nullptr) + { + // group object. Since the VOC/OC/VC hierarchy does not represent the + // hierarchy virtual objects when they have group objects + // (ViewContactOfVirtObj::GetObjectCount() returns null for that purpose) + // to avoid multiple usages of VOCs (which would not work), the primitives + // for the sub-hierarchy need to be collected here + + // Get the VOC of the referenced object (the Group) and fetch primitives from it + const ViewObjectContact& rVOCOfRefObj = rReferencedObject.GetViewContact().GetViewObjectContact(GetObjectContact()); + impAddPrimitivesFromGroup(rVOCOfRefObj, aOffsetMatrix, aDisplayInfo, xRetval); + } + else + { + // single object, use method from referenced object to get the Primitive2DSequence + rReferencedObject.GetViewContact().getViewIndependentPrimitive2DContainer(xRetval); + } + + if(!xRetval.empty()) + { + // create transform primitive + drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::TransformPrimitive2D(aOffsetMatrix, std::move(xRetval))); + xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference }; + } + + rVisitor.visit(xRetval); + } + + ViewObjectContact& VCOfDrawVirtObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) + { + return *(new VOCOfDrawVirtObj(rObjectContact, *this)); + } + +} // end of namespace sdr::contact + +/// implementation of class <SwDrawVirtObj> +std::unique_ptr<sdr::contact::ViewContact> SwDrawVirtObj::CreateObjectSpecificViewContact() +{ + return std::make_unique<sdr::contact::VCOfDrawVirtObj>(*this); +} + +SwDrawVirtObj::SwDrawVirtObj( + SdrModel& rSdrModel, + SdrObject& _rNewObj, + SwDrawContact& _rDrawContact) +: SdrVirtObj(rSdrModel, _rNewObj ), + mrDrawContact(_rDrawContact) +{ + // #i26791# + maAnchoredDrawObj.SetDrawObj( *this ); + + // #i35635# - set initial position out of sight + NbcMove( Size( -16000, -16000 ) ); +} + +SwDrawVirtObj::SwDrawVirtObj( + SdrModel& rSdrModel, + SwDrawVirtObj const & rSource) +: SdrVirtObj(rSdrModel, rSource), + mrDrawContact(rSource.mrDrawContact) +{ + // #i26791# + maAnchoredDrawObj.SetDrawObj( *this ); + + // #i35635# - set initial position out of sight + NbcMove( Size( -16000, -16000 ) ); + + // Note: Members <maAnchoredDrawObj> and <mrDrawContact> + // haven't to be considered. +} + +SwDrawVirtObj::~SwDrawVirtObj() +{ +} + +SwDrawVirtObj* SwDrawVirtObj::CloneSdrObject(SdrModel& rTargetModel) const +{ + return new SwDrawVirtObj(rTargetModel, *this); +} + +const SwFrame* SwDrawVirtObj::GetAnchorFrame() const +{ + // #i26791# - use new member <maAnchoredDrawObj> + return maAnchoredDrawObj.GetAnchorFrame(); +} + +SwFrame* SwDrawVirtObj::AnchorFrame() +{ + // #i26791# - use new member <maAnchoredDrawObj> + return maAnchoredDrawObj.AnchorFrame(); +} + +void SwDrawVirtObj::RemoveFromWriterLayout() +{ + // remove contact object from frame for 'virtual' drawing object + // #i26791# - use new member <maAnchoredDrawObj> + if ( maAnchoredDrawObj.GetAnchorFrame() ) + { + maAnchoredDrawObj.AnchorFrame()->RemoveDrawObj( maAnchoredDrawObj ); + } +} + +void SwDrawVirtObj::AddToDrawingPage(SwFrame const& rAnchorFrame) +{ + // determine 'master' + SdrObject* pOrgMasterSdrObj = mrDrawContact.GetMaster(); + + // insert 'virtual' drawing object into page, set layer and user call. + SdrPage* pDrawPg = pOrgMasterSdrObj->getSdrPageFromSdrObject(); + // default: insert before master object + auto nOrdNum(GetReferencedObj().GetOrdNum()); + + // maintain invariant that a shape's textbox immediately follows the shape + // also for the multiple SdrDrawVirtObj created for shapes in header/footer + if (SwFrameFormat const*const pFlyFormat = + SwTextBoxHelper::getOtherTextBoxFormat(mrDrawContact.GetFormat(), RES_DRAWFRMFMT)) + { + // this is for the case when the flyframe SdrVirtObj is created before the draw one + if (SwSortedObjs const*const pObjs = rAnchorFrame.GetDrawObjs()) + { + for (SwAnchoredObject const*const pAnchoredObj : *pObjs) + { + if (&pAnchoredObj->GetFrameFormat() == pFlyFormat) + { + assert(dynamic_cast<SwFlyFrame const*>(pAnchoredObj)); + nOrdNum = pAnchoredObj->GetDrawObj()->GetOrdNum(); + // the master SdrObj should have the highest index + assert(nOrdNum < GetReferencedObj().GetOrdNum()); + break; + } + } + } + // this happens on initial insertion, the draw object is created first + SAL_INFO_IF(GetReferencedObj().GetOrdNum() == nOrdNum, "sw", "AddToDrawingPage: cannot find SdrObject for text box's shape"); + } + + // #i27030# - apply order number of referenced object + if ( nullptr != pDrawPg ) + { + // #i27030# - apply order number of referenced object + pDrawPg->InsertObject(this, nOrdNum); + } + else + { + pDrawPg = getSdrPageFromSdrObject(); + if ( pDrawPg ) + { + pDrawPg->SetObjectOrdNum(GetOrdNumDirect(), nOrdNum); + } + else + { + SetOrdNum(nOrdNum); + } + } + SetUserCall( &mrDrawContact ); +} + +void SwDrawVirtObj::RemoveFromDrawingPage() +{ + SetUserCall( nullptr ); + if ( getSdrPageFromSdrObject() ) + { + getSdrPageFromSdrObject()->RemoveObject( GetOrdNum() ); + } +} + +/// Is 'virtual' drawing object connected to writer layout and to drawing layer? +bool SwDrawVirtObj::IsConnected() const +{ + bool bRetVal = GetAnchorFrame() && + ( getSdrPageFromSdrObject() && GetUserCall() ); + + return bRetVal; +} + +void SwDrawVirtObj::NbcSetAnchorPos(const Point& rPnt) +{ + SdrObject::NbcSetAnchorPos( rPnt ); +} + +// #i97197# +// the methods relevant for positioning + +const tools::Rectangle& SwDrawVirtObj::GetCurrentBoundRect() const +{ + if(m_aOutRect.IsEmpty()) + { + const_cast<SwDrawVirtObj*>(this)->RecalcBoundRect(); + } + + return m_aOutRect; +} + +const tools::Rectangle& SwDrawVirtObj::GetLastBoundRect() const +{ + return m_aOutRect; +} + +Point SwDrawVirtObj::GetOffset() const +{ + // do NOT use IsEmpty() here, there is already a useful offset + // in the position + if(m_aOutRect == tools::Rectangle()) + { + return Point(); + } + else + { + return m_aOutRect.TopLeft() - GetReferencedObj().GetCurrentBoundRect().TopLeft(); + } +} + +void SwDrawVirtObj::SetBoundRectDirty() +{ + // do nothing to not lose model information in aOutRect +} + +void SwDrawVirtObj::RecalcBoundRect() +{ + // #i26791# - switch order of calling <GetOffset()> and + // <ReferencedObj().GetCurrentBoundRect()>, because <GetOffset()> calculates + // its value by the 'BoundRect' of the referenced object. + + const Point aOffset(GetOffset()); + m_aOutRect = ReferencedObj().GetCurrentBoundRect() + aOffset; +} + +basegfx::B2DPolyPolygon SwDrawVirtObj::TakeXorPoly() const +{ + basegfx::B2DPolyPolygon aRetval(rRefObj.TakeXorPoly()); + aRetval.transform(basegfx::utils::createTranslateB2DHomMatrix(GetOffset().X(), GetOffset().Y())); + + return aRetval; +} + +basegfx::B2DPolyPolygon SwDrawVirtObj::TakeContour() const +{ + basegfx::B2DPolyPolygon aRetval(rRefObj.TakeContour()); + aRetval.transform(basegfx::utils::createTranslateB2DHomMatrix(GetOffset().X(), GetOffset().Y())); + + return aRetval; +} + +void SwDrawVirtObj::AddToHdlList(SdrHdlList& rHdlList) const +{ + SdrHdlList tmpList(nullptr); + rRefObj.AddToHdlList(tmpList); + + size_t cnt = tmpList.GetHdlCount(); + for(size_t i=0; i < cnt; ++i) + { + SdrHdl* pHdl = tmpList.GetHdl(i); + Point aP(pHdl->GetPos() + GetOffset()); + pHdl->SetPos(aP); + } + tmpList.MoveTo(rHdlList); +} + +void SwDrawVirtObj::NbcMove(const Size& rSiz) +{ + SdrObject::NbcMove( rSiz ); +} + +void SwDrawVirtObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) +{ + rRefObj.NbcResize(rRef - GetOffset(), xFact, yFact); + SetBoundAndSnapRectsDirty(); +} + +void SwDrawVirtObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs) +{ + rRefObj.NbcRotate(rRef - GetOffset(), nAngle, sn, cs); + SetBoundAndSnapRectsDirty(); +} + +void SwDrawVirtObj::NbcMirror(const Point& rRef1, const Point& rRef2) +{ + rRefObj.NbcMirror(rRef1 - GetOffset(), rRef2 - GetOffset()); + SetBoundAndSnapRectsDirty(); +} + +void SwDrawVirtObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear) +{ + rRefObj.NbcShear(rRef - GetOffset(), nAngle, tn, bVShear); + SetBoundAndSnapRectsDirty(); +} + +void SwDrawVirtObj::Move(const Size& rSiz) +{ + SdrObject::Move( rSiz ); +} + +void SwDrawVirtObj::Resize(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bUnsetRelative) +{ + if(xFact.GetNumerator() != xFact.GetDenominator() || yFact.GetNumerator() != yFact.GetDenominator()) + { + tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect(); + rRefObj.Resize(rRef - GetOffset(), xFact, yFact, bUnsetRelative); + SetBoundAndSnapRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); + } +} + +void SwDrawVirtObj::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs) +{ + if(nAngle) + { + tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect(); + rRefObj.Rotate(rRef - GetOffset(), nAngle, sn, cs); + SetBoundAndSnapRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); + } +} + +void SwDrawVirtObj::Mirror(const Point& rRef1, const Point& rRef2) +{ + tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect(); + rRefObj.Mirror(rRef1 - GetOffset(), rRef2 - GetOffset()); + SetBoundAndSnapRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); +} + +void SwDrawVirtObj::Shear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear) +{ + if(nAngle) + { + tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect(); + rRefObj.Shear(rRef - GetOffset(), nAngle, tn, bVShear); + SetBoundAndSnapRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); + } +} + +void SwDrawVirtObj::RecalcSnapRect() +{ + aSnapRect = rRefObj.GetSnapRect(); + aSnapRect += GetOffset(); +} + +const tools::Rectangle& SwDrawVirtObj::GetSnapRect() const +{ + const_cast<SwDrawVirtObj*>(this)->aSnapRect = rRefObj.GetSnapRect(); + const_cast<SwDrawVirtObj*>(this)->aSnapRect += GetOffset(); + + return aSnapRect; +} + +void SwDrawVirtObj::SetSnapRect(const tools::Rectangle& rRect) +{ + tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect(); + tools::Rectangle aR(rRect); + aR -= GetOffset(); + rRefObj.SetSnapRect(aR); + SetBoundAndSnapRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); +} + +void SwDrawVirtObj::NbcSetSnapRect(const tools::Rectangle& rRect) +{ + tools::Rectangle aR(rRect); + aR -= GetOffset(); + SetBoundAndSnapRectsDirty(); + rRefObj.NbcSetSnapRect(aR); +} + +const tools::Rectangle& SwDrawVirtObj::GetLogicRect() const +{ + const_cast<SwDrawVirtObj*>(this)->aSnapRect = rRefObj.GetLogicRect(); + const_cast<SwDrawVirtObj*>(this)->aSnapRect += GetOffset(); + + return aSnapRect; +} + +void SwDrawVirtObj::SetLogicRect(const tools::Rectangle& rRect) +{ + tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect(); + tools::Rectangle aR(rRect); + aR -= GetOffset(); + rRefObj.SetLogicRect(aR); + SetBoundAndSnapRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); +} + +void SwDrawVirtObj::NbcSetLogicRect(const tools::Rectangle& rRect) +{ + tools::Rectangle aR(rRect); + aR -= GetOffset(); + rRefObj.NbcSetLogicRect(aR); + SetBoundAndSnapRectsDirty(); +} + +Point SwDrawVirtObj::GetSnapPoint(sal_uInt32 i) const +{ + Point aP(rRefObj.GetSnapPoint(i)); + aP += GetOffset(); + + return aP; +} + +Point SwDrawVirtObj::GetPoint(sal_uInt32 i) const +{ + return rRefObj.GetPoint(i) + GetOffset(); +} + +void SwDrawVirtObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i) +{ + Point aP(rPnt); + aP -= GetOffset(); + rRefObj.SetPoint(aP, i); + SetBoundAndSnapRectsDirty(); +} + +bool SwDrawVirtObj::HasTextEdit() const +{ + return rRefObj.HasTextEdit(); +} + +// override 'layer' methods for 'virtual' drawing object to assure +// that layer of 'virtual' object is the layer of the referenced object. +SdrLayerID SwDrawVirtObj::GetLayer() const +{ + return GetReferencedObj().GetLayer(); +} + +void SwDrawVirtObj::NbcSetLayer(SdrLayerID nLayer) +{ + ReferencedObj().NbcSetLayer( nLayer ); + SdrVirtObj::NbcSetLayer( ReferencedObj().GetLayer() ); +} + +void SwDrawVirtObj::SetLayer(SdrLayerID nLayer) +{ + ReferencedObj().SetLayer( nLayer ); + SdrVirtObj::NbcSetLayer( ReferencedObj().GetLayer() ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/draw/dflyobj.cxx b/sw/source/core/draw/dflyobj.cxx new file mode 100644 index 000000000..3203bbc8d --- /dev/null +++ b/sw/source/core/draw/dflyobj.cxx @@ -0,0 +1,1295 @@ +/* -*- 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 <hintids.hxx> +#include <comphelper/lok.hxx> +#include <osl/diagnose.h> +#include <tools/mapunit.hxx> +#include <tools/UnitConversion.hxx> +#include <svx/svdhdl.hxx> +#include <svx/svdtrans.hxx> +#include <editeng/protitem.hxx> +#include <svx/svdpage.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/svapp.hxx> +#include <vcl/ptrstyle.hxx> + +#include <fmtclds.hxx> +#include <fmtornt.hxx> +#include <fmtfsize.hxx> +#include <fmturl.hxx> +#include <viewsh.hxx> +#include <frmatr.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <dflyobj.hxx> +#include <flyfrm.hxx> +#include <frmfmt.hxx> +#include <viewopt.hxx> +#include <frmtool.hxx> +#include <flyfrms.hxx> +#include <ndnotxt.hxx> +#include <grfatr.hxx> +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <textboxhelper.hxx> +#include <wrtsh.hxx> +#include <ndgrf.hxx> +#include <frmmgr.hxx> + +#include <svx/sdr/properties/defaultproperties.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + +// AW: For VCOfDrawVirtObj and stuff +#include <svx/sdr/contact/viewcontactofvirtobj.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <sw_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <notxtfrm.hxx> + +using namespace ::com::sun::star; + +static bool bInResize = false; + + +namespace sdr::contact +{ + namespace { + + /** + * @see #i95264# + * + * currently needed since createViewIndependentPrimitive2DSequence() is called when + * RecalcBoundRect() is used. There should currently no VOCs being constructed since it + * gets not visualized (instead the corresponding SwVirtFlyDrawObj's referencing this one + * are visualized). + */ + class VCOfSwFlyDrawObj : public ViewContactOfSdrObj + { + protected: + /** This method is responsible for creating the graphical visualisation data + * + * @note ONLY based on model data + */ + virtual void createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override; + + public: + /// basic constructor, used from SdrObject. + explicit VCOfSwFlyDrawObj(SwFlyDrawObj& rObj) + : ViewContactOfSdrObj(rObj) + { + } + }; + + } + + void VCOfSwFlyDrawObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor&) const + { + // currently gets not visualized, return empty sequence + } + +} // end of namespace sdr::contact + +std::unique_ptr<sdr::properties::BaseProperties> SwFlyDrawObj::CreateObjectSpecificProperties() +{ + // create default properties + return std::make_unique<sdr::properties::DefaultProperties>(*this); +} + +std::unique_ptr<sdr::contact::ViewContact> SwFlyDrawObj::CreateObjectSpecificViewContact() +{ + // needs an own VC since createViewIndependentPrimitive2DSequence() + // is called when RecalcBoundRect() is used + return std::make_unique<sdr::contact::VCOfSwFlyDrawObj>(*this); +} + +SwFlyDrawObj::SwFlyDrawObj(SdrModel& rSdrModel) +: SdrObject(rSdrModel), + mbIsTextBox(false) +{ +} + +SwFlyDrawObj::~SwFlyDrawObj() +{ +} + +// SwFlyDrawObj - Factory-Methods +SdrInventor SwFlyDrawObj::GetObjInventor() const +{ + return SdrInventor::Swg; +} + +SdrObjKind SwFlyDrawObj::GetObjIdentifier() const +{ + return SdrObjKind::SwFlyDrawObjIdentifier; +} + +// TODO: Need own primitive to get the FlyFrame paint working +namespace drawinglayer::primitive2d +{ + namespace { + + class SwVirtFlyDrawObjPrimitive : public BufferedDecompositionPrimitive2D + { + private: + const SwVirtFlyDrawObj& mrSwVirtFlyDrawObj; + const basegfx::B2DRange maOuterRange; + + protected: + /// method which is to be used to implement the local decomposition of a 2D primitive + virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const override; + + public: + SwVirtFlyDrawObjPrimitive( + const SwVirtFlyDrawObj& rSwVirtFlyDrawObj, + const basegfx::B2DRange &rOuterRange) + : mrSwVirtFlyDrawObj(rSwVirtFlyDrawObj), + maOuterRange(rOuterRange) + { + } + + virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; + + virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override; + + // override to allow callbacks to wrap_DoPaintObject + virtual void get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const override; + + // data read access + const SwVirtFlyDrawObj& getSwVirtFlyDrawObj() const { return mrSwVirtFlyDrawObj; } + const basegfx::B2DRange& getOuterRange() const { return maOuterRange; } + + /// provide unique ID + virtual sal_uInt32 getPrimitive2DID() const override; + }; + + } +} // end of namespace drawinglayer::primitive2d + +namespace drawinglayer::primitive2d +{ + void SwVirtFlyDrawObjPrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + { + if(getOuterRange().isEmpty()) + return; + + // currently this SW object has no primitive representation. As long as this is the case, + // create invisible geometry to allow correct HitTest and BoundRect calculations for the + // object. Use a filled primitive to get 'inside' as default object hit. The special cases from + // the old SwVirtFlyDrawObj::CheckHit implementation are handled now in SwDrawView::PickObj; + // this removed the 'hack' to get a view from inside model data or to react on null-tolerance + // as it was done in the old implementation + rContainer.push_back( + createHiddenGeometryPrimitives2D( + true, + getOuterRange())); + } + + bool SwVirtFlyDrawObjPrimitive::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SwVirtFlyDrawObjPrimitive& rCompare = static_cast<const SwVirtFlyDrawObjPrimitive&>(rPrimitive); + + return (&getSwVirtFlyDrawObj() == &rCompare.getSwVirtFlyDrawObj() + && getOuterRange() == rCompare.getOuterRange()); + } + + return false; + } + + basegfx::B2DRange SwVirtFlyDrawObjPrimitive::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const + { + return getOuterRange(); + } + + void SwVirtFlyDrawObjPrimitive::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const + { + // This is the callback to keep the FlyFrame painting in SW alive as long as it + // is not changed to primitives. This is the method which will be called by the processors + // when they do not know this primitive (and they do not). Inside wrap_DoPaintObject + // there needs to be a test that paint is only done during SW repaints (see there). + // Using this mechanism guarantees the correct Z-Order of the VirtualObject-based FlyFrames. + getSwVirtFlyDrawObj().wrap_DoPaintObject(rViewInformation); + + // call parent + BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); + } + + // provide unique ID + sal_uInt32 SwVirtFlyDrawObjPrimitive::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SWVIRTFLYDRAWOBJPRIMITIVE2D; + } + +} // end of namespace drawinglayer::primitive2d + +// AW: own sdr::contact::ViewContact (VC) sdr::contact::ViewObjectContact (VOC) needed +// since offset is defined different from SdrVirtObj's sdr::contact::ViewContactOfVirtObj. +// For paint, that offset is used by setting at the OutputDevice; for primitives this is +// not possible since we have no OutputDevice, but define the geometry itself. + +namespace sdr::contact +{ + namespace { + + class VCOfSwVirtFlyDrawObj : public ViewContactOfVirtObj + { + protected: + /** This method is responsible for creating the graphical visualisation data + * + * @note ONLY based on model data + */ + virtual void createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override; + + public: + /// basic constructor, used from SdrObject. + explicit VCOfSwVirtFlyDrawObj(SwVirtFlyDrawObj& rObj) + : ViewContactOfVirtObj(rObj) + { + } + + /// access to SwVirtFlyDrawObj + SwVirtFlyDrawObj& GetSwVirtFlyDrawObj() const + { + return static_cast<SwVirtFlyDrawObj&>(mrObject); + } + }; + + } +} // end of namespace sdr::contact + +namespace sdr::contact +{ + void VCOfSwVirtFlyDrawObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + const SdrObject& rReferencedObject = GetSwVirtFlyDrawObj().GetReferencedObj(); + + if(dynamic_cast<const SwFlyDrawObj*>( &rReferencedObject) != nullptr) + { + // create an own specialized primitive which is used as repaint callpoint and HitTest + // for HitTest processor (see primitive implementation above) + const basegfx::B2DRange aOuterRange(GetSwVirtFlyDrawObj().getOuterBound()); + + if(!aOuterRange.isEmpty()) + { + const drawinglayer::primitive2d::Primitive2DReference xPrimitive( + new drawinglayer::primitive2d::SwVirtFlyDrawObjPrimitive( + GetSwVirtFlyDrawObj(), + aOuterRange)); + + rVisitor.visit(xPrimitive); + } + } + } + +} // end of namespace sdr::contact + +basegfx::B2DRange SwVirtFlyDrawObj::getOuterBound() const +{ + basegfx::B2DRange aOuterRange; + const SdrObject& rReferencedObject = GetReferencedObj(); + + if(dynamic_cast<const SwFlyDrawObj*>( &rReferencedObject) != nullptr) + { + const SwFlyFrame* pFlyFrame = GetFlyFrame(); + + if(pFlyFrame) + { + const tools::Rectangle aOuterRectangle(pFlyFrame->getFrameArea().Pos(), pFlyFrame->getFrameArea().SSize()); + + if(!aOuterRectangle.IsEmpty()) + { + aOuterRange.expand(basegfx::B2DTuple(aOuterRectangle.Left(), aOuterRectangle.Top())); + aOuterRange.expand(basegfx::B2DTuple(aOuterRectangle.Right(), aOuterRectangle.Bottom())); + } + } + } + + return aOuterRange; +} + +basegfx::B2DRange SwVirtFlyDrawObj::getInnerBound() const +{ + basegfx::B2DRange aInnerRange; + const SdrObject& rReferencedObject = GetReferencedObj(); + + if(dynamic_cast<const SwFlyDrawObj*>( &rReferencedObject) != nullptr) + { + const SwFlyFrame* pFlyFrame = GetFlyFrame(); + + if(pFlyFrame) + { + const tools::Rectangle aInnerRectangle(pFlyFrame->getFrameArea().Pos() + pFlyFrame->getFramePrintArea().Pos(), pFlyFrame->getFramePrintArea().SSize()); + + if(!aInnerRectangle.IsEmpty()) + { + aInnerRange.expand(basegfx::B2DTuple(aInnerRectangle.Left(), aInnerRectangle.Top())); + aInnerRange.expand(basegfx::B2DTuple(aInnerRectangle.Right(), aInnerRectangle.Bottom())); + } + } + } + + return aInnerRange; +} + +bool SwVirtFlyDrawObj::ContainsSwGrfNode() const +{ + // RotGrfFlyFrame: Check if this is a SwGrfNode + const SwFlyFrame* pFlyFrame(GetFlyFrame()); + + if(nullptr != pFlyFrame && pFlyFrame->Lower() && pFlyFrame->Lower()->IsNoTextFrame()) + { + const SwNoTextFrame *const pNTF(static_cast<const SwNoTextFrame*>(pFlyFrame->Lower())); + + const SwGrfNode *const pGrfNd(pNTF->GetNode()->GetGrfNode()); + + return nullptr != pGrfNd; + } + + return false; +} + +bool SwVirtFlyDrawObj::HasLimitedRotation() const +{ + // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation. + // This is the case for SwGrfNode instances + return ContainsSwGrfNode(); +} + +void SwVirtFlyDrawObj::Rotate(const Point& rRef, Degree100 nAngle100, double sn, double cs) +{ + if(ContainsSwGrfNode()) + { + // RotGrfFlyFrame: Here is where the positively completed rotate interaction is executed. + // Rotation is in 1/100th degree and may be signed (!) + Degree10 nAngle10 = to<Degree10>(nAngle100); + + while(nAngle10 < 0_deg10) + { + nAngle10 += 3600_deg10; + } + + SwWrtShell *pShForAngle = nAngle10 ? dynamic_cast<SwWrtShell*>(GetFlyFrame()->getRootFrame()->GetCurrShell()) : nullptr; + if (pShForAngle) + { + // RotGrfFlyFrame: Add transformation to placeholder object + Size aSize; + const Degree10 nOldRot(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize)); + SwFlyFrameAttrMgr aMgr(false, pShForAngle, Frmmgr_Type::NONE, nullptr); + + aMgr.SetRotation(nOldRot, (nOldRot + nAngle10) % 3600_deg10, aSize); + } + } + else + { + // call parent + SdrVirtObj::Rotate(rRef, nAngle100, sn, cs); + } +} + +std::unique_ptr<sdr::contact::ViewContact> SwVirtFlyDrawObj::CreateObjectSpecificViewContact() +{ + // need an own ViewContact (VC) to allow creation of a specialized primitive + // for being able to visualize the FlyFrames in primitive renderers + return std::make_unique<sdr::contact::VCOfSwVirtFlyDrawObj>(*this); +} + +SwVirtFlyDrawObj::SwVirtFlyDrawObj( + SdrModel& rSdrModel, + SdrObject& rNew, + SwFlyFrame* pFly) +: SdrVirtObj(rSdrModel, rNew), + m_pFlyFrame(pFly) +{ + const SvxProtectItem &rP = m_pFlyFrame->GetFormat()->GetProtect(); + m_bMovProt = rP.IsPosProtected(); + m_bSizProt = rP.IsSizeProtected(); +} + +SwVirtFlyDrawObj::~SwVirtFlyDrawObj() +{ + if ( getSdrPageFromSdrObject() ) //Withdraw SdrPage the responsibility. + getSdrPageFromSdrObject()->RemoveObject( GetOrdNum() ); +} + +const SwFrameFormat *SwVirtFlyDrawObj::GetFormat() const +{ + return GetFlyFrame()->GetFormat(); +} +SwFrameFormat *SwVirtFlyDrawObj::GetFormat() +{ + return GetFlyFrame()->GetFormat(); +} + +// --> OD #i102707# +namespace +{ + class RestoreMapMode + { + public: + explicit RestoreMapMode( SwViewShell const * pViewShell ) + : mbMapModeRestored( false ) + , mpOutDev( pViewShell->GetOut() ) + { + if ( pViewShell->getPrePostMapMode() == mpOutDev->GetMapMode() ) + return; + + mpOutDev->Push(vcl::PushFlags::MAPMODE); + + GDIMetaFile* pMetaFile = mpOutDev->GetConnectMetaFile(); + if ( pMetaFile && + pMetaFile->IsRecord() && !pMetaFile->IsPause() ) + { + OSL_FAIL( "MapMode restoration during meta file creation is somehow suspect - using <SetRelativeMapMode(..)>, but not sure, if correct." ); + mpOutDev->SetRelativeMapMode( pViewShell->getPrePostMapMode() ); + } + else + { + mpOutDev->SetMapMode( pViewShell->getPrePostMapMode() ); + } + + mbMapModeRestored = true; + }; + + ~RestoreMapMode() + { + if ( mbMapModeRestored ) + { + mpOutDev->Pop(); + } + }; + + private: + bool mbMapModeRestored; + VclPtr<OutputDevice> mpOutDev; + }; +} +// <-- + +void SwVirtFlyDrawObj::wrap_DoPaintObject( + drawinglayer::geometry::ViewInformation2D const& rViewInformation) const +{ + SwViewShell* pShell = m_pFlyFrame->getRootFrame()->GetCurrShell(); + + // Only paint when we have a current shell and a DrawingLayer paint is in progress. + // This avoids evtl. problems with renderers which do processing stuff, + // but no paints. IsPaintInProgress() depends on SW repaint, so, as long + // as SW paints self and calls DrawLayer() for Heaven and Hell, this will + // be correct + if ( !(pShell && pShell->IsDrawingLayerPaintInProgress()) ) + return; + + bool bDrawObject(true); + + if ( !SwFlyFrame::IsPaint( const_cast<SwVirtFlyDrawObj*>(this), pShell ) ) + { + bDrawObject = false; + } + + if ( !bDrawObject ) + return; + + // if there's no viewport set, all fly-frames will be painted, + // which is slow, wastes memory, and can cause other trouble. + (void) rViewInformation; // suppress "unused parameter" warning + assert(comphelper::LibreOfficeKit::isActive() || !rViewInformation.getViewport().isEmpty()); + if ( m_pFlyFrame->IsFlyInContentFrame() ) + return; + + // it is also necessary to restore the VCL MapMode from ViewInformation since e.g. + // the VCL PixelRenderer resets it at the used OutputDevice. Unfortunately, this + // excludes shears and rotates which are not expressible in MapMode. + // OD #i102707# + // new helper class to restore MapMode - restoration, only if + // needed and consideration of paint for meta file creation . + RestoreMapMode aRestoreMapModeIfNeeded( pShell ); + + // paint the FlyFrame (use standard VCL-Paint) + m_pFlyFrame->PaintSwFrame( *pShell->GetOut(), m_pFlyFrame->GetPageFrame()->getFrameArea()); +} + +void SwVirtFlyDrawObj::TakeObjInfo( SdrObjTransformInfoRec& rInfo ) const +{ + rInfo.bMoveAllowed = + rInfo.bResizeFreeAllowed = rInfo.bResizePropAllowed = true; + + // RotGrfFlyFrame: Some rotation may be allowed + rInfo.bRotateFreeAllowed = rInfo.bRotate90Allowed = HasLimitedRotation(); + + rInfo.bMirrorFreeAllowed = rInfo.bMirror45Allowed = + rInfo.bMirror90Allowed = rInfo.bShearAllowed = + rInfo.bCanConvToPath = rInfo.bCanConvToPoly = + rInfo.bCanConvToPathLineToArea = rInfo.bCanConvToPolyLineToArea = false; +} + +// SwVirtFlyDrawObj - Size Determination + +void SwVirtFlyDrawObj::SetRect() const +{ + if ( GetFlyFrame()->getFrameArea().HasArea() ) + const_cast<SwVirtFlyDrawObj*>(this)->m_aOutRect = GetFlyFrame()->getFrameArea().SVRect(); + else + const_cast<SwVirtFlyDrawObj*>(this)->m_aOutRect = tools::Rectangle(); +} + +const tools::Rectangle& SwVirtFlyDrawObj::GetCurrentBoundRect() const +{ + SetRect(); + return m_aOutRect; +} + +const tools::Rectangle& SwVirtFlyDrawObj::GetLastBoundRect() const +{ + return GetCurrentBoundRect(); +} + +void SwVirtFlyDrawObj::RecalcBoundRect() +{ + SetRect(); +} + +void SwVirtFlyDrawObj::RecalcSnapRect() +{ + SetRect(); +} + +const tools::Rectangle& SwVirtFlyDrawObj::GetSnapRect() const +{ + SetRect(); + return m_aOutRect; +} + +void SwVirtFlyDrawObj::SetSnapRect(const tools::Rectangle& ) +{ + tools::Rectangle aTmp( GetLastBoundRect() ); + SetRect(); + SetChanged(); + BroadcastObjectChange(); + if (m_pUserCall!=nullptr) + m_pUserCall->Changed(*this, SdrUserCallType::Resize, aTmp); +} + +void SwVirtFlyDrawObj::NbcSetSnapRect(const tools::Rectangle& ) +{ + SetRect(); +} + +const tools::Rectangle& SwVirtFlyDrawObj::GetLogicRect() const +{ + SetRect(); + return m_aOutRect; +} + +void SwVirtFlyDrawObj::SetLogicRect(const tools::Rectangle& ) +{ + tools::Rectangle aTmp( GetLastBoundRect() ); + SetRect(); + SetChanged(); + BroadcastObjectChange(); + if (m_pUserCall!=nullptr) + m_pUserCall->Changed(*this, SdrUserCallType::Resize, aTmp); +} + +void SwVirtFlyDrawObj::NbcSetLogicRect(const tools::Rectangle& ) +{ + SetRect(); +} + +::basegfx::B2DPolyPolygon SwVirtFlyDrawObj::TakeXorPoly() const +{ + const tools::Rectangle aSourceRectangle(GetFlyFrame()->getFrameArea().SVRect()); + const ::basegfx::B2DRange aSourceRange = vcl::unotools::b2DRectangleFromRectangle(aSourceRectangle); + ::basegfx::B2DPolyPolygon aRetval; + + aRetval.append(::basegfx::utils::createPolygonFromRect(aSourceRange)); + + return aRetval; +} + +// SwVirtFlyDrawObj::Move() and Resize() +void SwVirtFlyDrawObj::NbcMove(const Size& rSiz) +{ + if(GetFlyFrame()->IsFlyFreeFrame() && static_cast< SwFlyFreeFrame* >(GetFlyFrame())->isTransformableSwFrame()) + { + // RotateFlyFrame3: When we have a change and are in transformed state (e.g. rotation used), + // we need to fall back to the un-transformed state to keep the old code below + // working properly. Restore FrameArea and use aOutRect from old FrameArea. + TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(GetFlyFrame())->getTransformableSwFrame()); + pTransformableSwFrame->restoreFrameAreas(); + m_aOutRect = GetFlyFrame()->getFrameArea().SVRect(); + } + + m_aOutRect.Move( rSiz ); + const Point aOldPos( GetFlyFrame()->getFrameArea().Pos() ); + const Point aNewPos( m_aOutRect.TopLeft() ); + const SwRect aFlyRect( m_aOutRect ); + + //If the Fly has an automatic align (right or top), + //so preserve the automatic. + SwFrameFormat *pFormat = GetFlyFrame()->GetFormat(); + const sal_Int16 eHori = pFormat->GetHoriOrient().GetHoriOrient(); + const sal_Int16 eVert = pFormat->GetVertOrient().GetVertOrient(); + const sal_Int16 eRelHori = pFormat->GetHoriOrient().GetRelationOrient(); + const sal_Int16 eRelVert = pFormat->GetVertOrient().GetRelationOrient(); + //On paragraph bound Flys starting from the new position a new + //anchor must be set. Anchor and the new RelPos is calculated and + //placed by the Fly itself. + if( GetFlyFrame()->IsFlyAtContentFrame() ) + { + static_cast<SwFlyAtContentFrame*>(GetFlyFrame())->SetAbsPos( aNewPos ); + } + else + { + const SwFrameFormat *pTmpFormat = GetFormat(); + const SwFormatVertOrient &rVert = pTmpFormat->GetVertOrient(); + const SwFormatHoriOrient &rHori = pTmpFormat->GetHoriOrient(); + tools::Long lXDiff = aNewPos.X() - aOldPos.X(); + if( rHori.IsPosToggle() && text::HoriOrientation::NONE == eHori && + !GetFlyFrame()->FindPageFrame()->OnRightPage() ) + lXDiff = -lXDiff; + + if( GetFlyFrame()->GetAnchorFrame()->IsRightToLeft() && + text::HoriOrientation::NONE == eHori ) + lXDiff = -lXDiff; + + tools::Long lYDiff = aNewPos.Y() - aOldPos.Y(); + if( GetFlyFrame()->GetAnchorFrame()->IsVertical() ) + { + //lXDiff -= rVert.GetPos(); + //lYDiff += rHori.GetPos(); + + if ( GetFlyFrame()->GetAnchorFrame()->IsVertLR() ) + { + lXDiff += rVert.GetPos(); + lXDiff = -lXDiff; + } + else + { + lXDiff -= rVert.GetPos(); + lYDiff += rHori.GetPos(); + } + } + else + { + lXDiff += rHori.GetPos(); + lYDiff += rVert.GetPos(); + } + + if( GetFlyFrame()->GetAnchorFrame()->IsRightToLeft() && + text::HoriOrientation::NONE != eHori ) + lXDiff = GetFlyFrame()->GetAnchorFrame()->getFrameArea().Width() - + aFlyRect.Width() - lXDiff; + + const Point aTmp( lXDiff, lYDiff ); + GetFlyFrame()->ChgRelPos( aTmp ); + } + + SwAttrSet aSet( pFormat->GetDoc()->GetAttrPool(), + RES_VERT_ORIENT, RES_HORI_ORIENT ); + SwFormatHoriOrient aHori( pFormat->GetHoriOrient() ); + SwFormatVertOrient aVert( pFormat->GetVertOrient() ); + bool bPut = false; + + if( !GetFlyFrame()->IsFlyLayFrame() && + ::GetHtmlMode(pFormat->GetDoc()->GetDocShell()) ) + { + //In HTML-Mode only automatic aligns are allowed. + //Only we can try a snap to left/right respectively left-/right border + const SwFrame* pAnch = GetFlyFrame()->GetAnchorFrame(); + bool bNextLine = false; + + if( !GetFlyFrame()->IsAutoPos() || text::RelOrientation::PAGE_FRAME != aHori.GetRelationOrient() ) + { + if( text::RelOrientation::CHAR == eRelHori ) + { + aHori.SetHoriOrient( text::HoriOrientation::LEFT ); + aHori.SetRelationOrient( text::RelOrientation::CHAR ); + } + else + { + bNextLine = true; + //Horizontal Align: + const bool bLeftFrame = + aFlyRect.Left() < pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Left(), + bLeftPrt = aFlyRect.Left() + aFlyRect.Width() < + pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Width()/2; + if ( bLeftFrame || bLeftPrt ) + { + aHori.SetHoriOrient( text::HoriOrientation::LEFT ); + aHori.SetRelationOrient( bLeftFrame ? text::RelOrientation::FRAME : text::RelOrientation::PRINT_AREA ); + } + else + { + const bool bRightFrame = aFlyRect.Left() > + pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Width(); + aHori.SetHoriOrient( text::HoriOrientation::RIGHT ); + aHori.SetRelationOrient( bRightFrame ? text::RelOrientation::FRAME : text::RelOrientation::PRINT_AREA ); + } + } + aSet.Put( aHori ); + } + //Vertical alignment simply is retained principally, + //only on manual align will be switched over. + bool bRelChar = text::RelOrientation::CHAR == eRelVert; + aVert.SetVertOrient( eVert != text::VertOrientation::NONE ? eVert : + GetFlyFrame()->IsFlyInContentFrame() ? text::VertOrientation::CHAR_CENTER : + bRelChar && bNextLine ? text::VertOrientation::CHAR_TOP : text::VertOrientation::TOP ); + if( bRelChar ) + aVert.SetRelationOrient( text::RelOrientation::CHAR ); + else + aVert.SetRelationOrient( text::RelOrientation::PRINT_AREA ); + aSet.Put( aVert ); + bPut = true; + } + + //We want preferably not to lose the automatic alignments. + if ( !bPut && bInResize ) + { + if ( text::HoriOrientation::NONE != eHori ) + { + aHori.SetHoriOrient( eHori ); + aHori.SetRelationOrient( eRelHori ); + aSet.Put( aHori ); + bPut = true; + } + if ( text::VertOrientation::NONE != eVert ) + { + aVert.SetVertOrient( eVert ); + aVert.SetRelationOrient( eRelVert ); + aSet.Put( aVert ); + bPut = true; + } + } + if ( bPut ) + pFormat->SetFormatAttr( aSet ); +} + + +void SwVirtFlyDrawObj::NbcCrop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact) +{ + // Get Wrt Shell + SwWrtShell *pSh = dynamic_cast<SwWrtShell*>( GetFlyFrame()->getRootFrame()->GetCurrShell() ); + + if (!pSh) + { + return; + } + + GraphicObject const *pGraphicObject = pSh->GetGraphicObj(); + + if (!pGraphicObject) + { + return; + } + + // Get graphic object size in 100th of mm + const MapMode aMapMode100thmm(MapUnit::Map100thMM); + Size aGraphicSize(pGraphicObject->GetPrefSize()); + + if( MapUnit::MapPixel == pGraphicObject->GetPrefMapMode().GetMapUnit() ) + { + aGraphicSize = Application::GetDefaultDevice()->PixelToLogic( aGraphicSize, aMapMode100thmm ); + } + else + { + aGraphicSize = OutputDevice::LogicToLogic( aGraphicSize, pGraphicObject->GetPrefMapMode(), aMapMode100thmm); + } + + if( aGraphicSize.IsEmpty() ) + { + return ; + } + + const bool bIsTransformableSwFrame( + GetFlyFrame()->IsFlyFreeFrame() && + static_cast< SwFlyFreeFrame* >(GetFlyFrame())->isTransformableSwFrame()); + + if(bIsTransformableSwFrame) + { + // When we have a change and are in transformed state (e.g. rotation used), + // we need to fall back to the un-transformed state to keep the old code below + // working properly. Restore FrameArea and use aOutRect from old FrameArea. + TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(GetFlyFrame())->getTransformableSwFrame()); + pTransformableSwFrame->restoreFrameAreas(); + m_aOutRect = GetFlyFrame()->getFrameArea().SVRect(); + } + + // Compute old and new rect. This will give us the deformation to apply to + // the object to crop. OldRect is the inner frame, see getFullDragClone() + // below where getFramePrintAreaTransformation is used as object geometry for Crop + const tools::Rectangle aOldRect( + GetFlyFrame()->getFrameArea().TopLeft() + GetFlyFrame()->getFramePrintArea().TopLeft(), + GetFlyFrame()->getFramePrintArea().SSize()); + const tools::Long nOldWidth(aOldRect.GetWidth()); + const tools::Long nOldHeight(aOldRect.GetHeight()); + + if (!nOldWidth || !nOldHeight) + { + return; + } + + // rRef is relative to the Crop-Action, si in X/Y-Ranges of [0.0 .. 1.0], + // to get the correct absolute position, transform using the old Rect + const Point aRef( + aOldRect.Left() + basegfx::fround(aOldRect.GetWidth() * rRef.getX()), + aOldRect.Top() + basegfx::fround(aOldRect.GetHeight() * rRef.getY())); + + // apply transformation, use old ResizeRect for now + tools::Rectangle aNewRect( aOldRect ); + ResizeRect( + aNewRect, + aRef, + Fraction(fxFact), + Fraction(fyFact)); + + // Get old values for crop in 10th of mm + SfxItemSetFixed<RES_GRFATR_CROPGRF, RES_GRFATR_CROPGRF> aSet( pSh->GetAttrPool() ); + pSh->GetCurAttr( aSet ); + SwCropGrf aCrop( aSet.Get(RES_GRFATR_CROPGRF) ); + + tools::Rectangle aCropRectangle( + convertTwipToMm100(aCrop.GetLeft()), + convertTwipToMm100(aCrop.GetTop()), + convertTwipToMm100(aCrop.GetRight()), + convertTwipToMm100(aCrop.GetBottom()) ); + + // Compute delta to apply + double fScaleX = ( aGraphicSize.Width() - aCropRectangle.Left() - aCropRectangle.Right() ) / static_cast<double>(nOldWidth); + double fScaleY = ( aGraphicSize.Height() - aCropRectangle.Top() - aCropRectangle.Bottom() ) / static_cast<double>(nOldHeight); + + sal_Int32 nDiffLeft = aNewRect.Left() - aOldRect.Left(); + sal_Int32 nDiffTop = aNewRect.Top() - aOldRect.Top(); + sal_Int32 nDiffRight = aNewRect.Right() - aOldRect.Right(); + sal_Int32 nDiffBottom = aNewRect.Bottom() - aOldRect.Bottom(); + + // Compute new values in 10th of mm + sal_Int32 nLeftCrop = static_cast<sal_Int32>( aCropRectangle.Left() + nDiffLeft * fScaleX ); + sal_Int32 nTopCrop = static_cast<sal_Int32>( aCropRectangle.Top() + nDiffTop * fScaleY ); + sal_Int32 nRightCrop = static_cast<sal_Int32>( aCropRectangle.Right() - nDiffRight * fScaleX ); + sal_Int32 nBottomCrop = static_cast<sal_Int32>( aCropRectangle.Bottom() - nDiffBottom * fScaleY ); + + // Apply values + pSh->StartAllAction(); + // pSh->StartUndo(SwUndoId::START); + + // Set new crop values in twips + aCrop.SetLeft (o3tl::toTwips(nLeftCrop, o3tl::Length::mm100)); + aCrop.SetTop (o3tl::toTwips(nTopCrop, o3tl::Length::mm100)); + aCrop.SetRight (o3tl::toTwips(nRightCrop, o3tl::Length::mm100)); + aCrop.SetBottom(o3tl::toTwips(nBottomCrop, o3tl::Length::mm100)); + pSh->SetAttrItem(aCrop); + + // Set new frame size + SwFrameFormat *pFormat = GetFormat(); + SwFormatFrameSize aSz( pFormat->GetFrameSize() ); + const tools::Long aNewWidth(aNewRect.GetWidth() + (m_aOutRect.GetWidth() - aOldRect.GetWidth())); + const tools::Long aNewHeight(aNewRect.GetHeight() + (m_aOutRect.GetHeight() - aOldRect.GetHeight())); + aSz.SetWidth(aNewWidth); + aSz.SetHeight(aNewHeight); + pFormat->GetDoc()->SetAttr( aSz, *pFormat ); + + // add move - to make result look better. Fill with defaults + // for the untransformed case + Point aNewTopLeft(aNewRect.TopLeft()); + const Point aOldTopLeft(aOldRect.TopLeft()); + + if(bIsTransformableSwFrame) + { + // Need to correct the NewTopLeft position in transformed state to make + // the interaction look correct. First, extract rotation + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + GetFlyFrame()->getFrameAreaTransformation().decompose(aScale, aTranslate, fRotate, fShearX); + + // calc the center of the unchanged object + const basegfx::B2DPoint aFormerCenter( + GetFlyFrame()->getFrameAreaTransformation() * basegfx::B2DPoint(0.5, 0.5)); + + // define the existing rotation around that former center + const basegfx::B2DHomMatrix aRotFormerCenter( + basegfx::utils::createRotateAroundPoint( + aFormerCenter.getX(), + aFormerCenter.getY(), + fRotate)); + + // use the new center of the unrotated object, rotate it around the + // former center + const Point aNewCenter(aNewRect.Center()); + const basegfx::B2DPoint aRotNewCenter( + aRotFormerCenter * basegfx::B2DPoint(aNewCenter.X(), aNewCenter.Y())); + + // Create the new TopLeft of the unrotated, cropped object by creating + // as if re-creating the unrotated geometry + aNewTopLeft = Point( + basegfx::fround(aRotNewCenter.getX() - (0.5 * aNewRect.getWidth())), + basegfx::fround(aRotNewCenter.getY() - (0.5 * aNewRect.getHeight()))); + } + + // check if we have movement and execute if yes + const Size aDeltaMove( + aNewTopLeft.X() - aOldTopLeft.X(), + aNewTopLeft.Y() - aOldTopLeft.Y()); + + if(0 != aDeltaMove.Width() || 0 != aDeltaMove.Height()) + { + NbcMove(aDeltaMove); + } + + // pSh->EndUndo(SwUndoId::END); + pSh->EndAllAction(); +} + +void SwVirtFlyDrawObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) +{ + const SwFrame* pTmpFrame = GetFlyFrame()->GetAnchorFrame(); + + if( !pTmpFrame ) + { + pTmpFrame = GetFlyFrame(); + } + + const bool bVertX(pTmpFrame->IsVertical()); + const bool bRTL(pTmpFrame->IsRightToLeft()); + const bool bVertL2RX(pTmpFrame->IsVertLR()); + const bool bUseRightEdge((bVertX && !bVertL2RX ) || bRTL); + const bool bIsTransformableSwFrame( + GetFlyFrame()->IsFlyFreeFrame() && + static_cast< SwFlyFreeFrame* >(GetFlyFrame())->isTransformableSwFrame()); + + if(bIsTransformableSwFrame) + { + // When we have a change in transformed state, we need to fall back to the + // state without possible transformations. + // In the Resize case to correctly handle the changes, apply to the transformation + // and extract the new, untransformed state from that modified transformation + basegfx::B2DHomMatrix aNewMat(GetFlyFrame()->getFrameAreaTransformation()); + const basegfx::B2DPoint aRef(rRef.X(), rRef.Y()); + + // apply state to already valid transformation + aNewMat.translate(-aRef.getX(), -aRef.getY()); + aNewMat.scale(double(xFact), double(yFact)); + aNewMat.translate(aRef.getX(), aRef.getY()); + + // get center of transformed state + const basegfx::B2DPoint aCenter(aNewMat * basegfx::B2DPoint(0.5, 0.5)); + + // decompose to extract scale + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aNewMat.decompose(aScale, aTranslate, fRotate, fShearX); + const basegfx::B2DVector aAbsScale(basegfx::absolute(aScale)); + + // create new modified, but untransformed OutRect + m_aOutRect = tools::Rectangle( + basegfx::fround(aCenter.getX() - (0.5 * aAbsScale.getX())), + basegfx::fround(aCenter.getY() - (0.5 * aAbsScale.getY())), + basegfx::fround(aCenter.getX() + (0.5 * aAbsScale.getX())), + basegfx::fround(aCenter.getY() + (0.5 * aAbsScale.getY()))); + + // restore FrameAreas so that actions below not adapted to new + // full transformations take the correct actions + TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(GetFlyFrame())->getTransformableSwFrame()); + pTransformableSwFrame->restoreFrameAreas(); + } + else + { + ResizeRect( m_aOutRect, rRef, xFact, yFact ); + } + + // Position may also change, remember old one. This is now already + // the one in the unrotated, old coordinate system + Point aOldPos(bUseRightEdge ? GetFlyFrame()->getFrameArea().TopRight() : GetFlyFrame()->getFrameArea().Pos()); + + // get target size in old coordinate system + Size aSz( m_aOutRect.Right() - m_aOutRect.Left() + 1, m_aOutRect.Bottom()- m_aOutRect.Top() + 1 ); + + // compare with restored FrameArea + if( aSz != GetFlyFrame()->getFrameArea().SSize() ) + { + //The width of the columns should not be too narrow + if ( GetFlyFrame()->Lower() && GetFlyFrame()->Lower()->IsColumnFrame() ) + { + SwBorderAttrAccess aAccess( SwFrame::GetCache(), GetFlyFrame() ); + const SwBorderAttrs &rAttrs = *aAccess.Get(); + tools::Long nMin = rAttrs.CalcLeftLine()+rAttrs.CalcRightLine(); + const SwFormatCol& rCol = rAttrs.GetAttrSet().GetCol(); + if ( rCol.GetColumns().size() > 1 ) + { + for ( const auto &rC : rCol.GetColumns() ) + { + nMin += rC.GetLeft() + rC.GetRight() + MINFLY; + } + nMin -= MINFLY; + } + aSz.setWidth( std::max( aSz.Width(), nMin ) ); + } + + SwFrameFormat *pFormat = GetFormat(); + const SwFormatFrameSize aOldFrameSz( pFormat->GetFrameSize() ); + GetFlyFrame()->ChgSize( aSz ); + SwFormatFrameSize aFrameSz( pFormat->GetFrameSize() ); + + if ( aFrameSz.GetWidthPercent() || aFrameSz.GetHeightPercent() ) + { + tools::Long nRelWidth, nRelHeight; + const SwFrame *pRel = GetFlyFrame()->IsFlyLayFrame() ? + GetFlyFrame()->GetAnchorFrame() : + GetFlyFrame()->GetAnchorFrame()->GetUpper(); + const SwViewShell *pSh = GetFlyFrame()->getRootFrame()->GetCurrShell(); + + if ( pSh && pRel->IsBodyFrame() && + pSh->GetViewOptions()->getBrowseMode() && + pSh->VisArea().HasArea() ) + { + nRelWidth = pSh->GetBrowseWidth(); + nRelHeight = pSh->VisArea().Height(); + const Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() ); + nRelHeight -= 2*aBorder.Height(); + } + else + { + nRelWidth = pRel->getFramePrintArea().Width(); + nRelHeight = pRel->getFramePrintArea().Height(); + } + + if ( aFrameSz.GetWidthPercent() && aFrameSz.GetWidthPercent() != SwFormatFrameSize::SYNCED && + aOldFrameSz.GetWidth() != aFrameSz.GetWidth() ) + { + aFrameSz.SetWidthPercent( sal_uInt8(aSz.Width() * 100.0 / nRelWidth + 0.5) ); + } + + if ( aFrameSz.GetHeightPercent() && aFrameSz.GetHeightPercent() != SwFormatFrameSize::SYNCED && + aOldFrameSz.GetHeight() != aFrameSz.GetHeight() ) + { + aFrameSz.SetHeightPercent( sal_uInt8(aSz.Height() * 100.0 / nRelHeight + 0.5) ); + } + + pFormat->GetDoc()->SetAttr( aFrameSz, *pFormat ); + } + } + + //Position can also be changed, get new one + const Point aNewPos(bUseRightEdge ? m_aOutRect.Right() + 1 : m_aOutRect.Left(), m_aOutRect.Top()); + + if ( aNewPos == aOldPos ) + return; + + // Former late change in aOutRect by ChgSize + // is now taken into account directly by calculating + // aNewPos *after* calling ChgSize (see old code). + // Still need to adapt aOutRect since the 'Move' is already applied + // here (see ResizeRect) and it's the same SdrObject + const Size aDeltaMove( + aNewPos.X() - aOldPos.X(), + aNewPos.Y() - aOldPos.Y()); + m_aOutRect.Move(-aDeltaMove.Width(), -aDeltaMove.Height()); + + // Now, move as needed (no empty delta which was a hack anyways) + if(bIsTransformableSwFrame) + { + // need to save aOutRect to FrameArea, will be restored to aOutRect in + // SwVirtFlyDrawObj::NbcMove currently for TransformableSwFrames + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*GetFlyFrame()); + aFrm.setSwRect(SwRect(m_aOutRect)); + } + + // keep old hack - not clear what happens here + bInResize = true; + NbcMove(aDeltaMove); + bInResize = false; +} + +void SwVirtFlyDrawObj::Move(const Size& rSiz) +{ + NbcMove( rSiz ); + SetChanged(); + GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false); +} + +void SwVirtFlyDrawObj::Resize(const Point& rRef, + const Fraction& xFact, const Fraction& yFact, bool /*bUnsetRelative*/) +{ + NbcResize( rRef, xFact, yFact ); + SetChanged(); + GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false); +} + +void SwVirtFlyDrawObj::Crop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact) +{ + NbcCrop( rRef, fxFact, fyFact ); + SetChanged(); + GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false); +} + +// RotGrfFlyFrame: Helper to access possible rotation of Graphic contained in FlyFrame +Degree10 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(Size& rSize) const +{ + Degree10 nRetval; + const SwNoTextFrame* pNoTx = dynamic_cast< const SwNoTextFrame* >(GetFlyFrame()->Lower()); + + if(pNoTx) + { + SwNoTextNode& rNoTNd = const_cast< SwNoTextNode& >(*static_cast<const SwNoTextNode*>(pNoTx->GetNode())); + SwGrfNode* pGrfNd = rNoTNd.GetGrfNode(); + + if(nullptr != pGrfNd) + { + const SwAttrSet& rSet = pGrfNd->GetSwAttrSet(); + const SwRotationGrf& rRotation = rSet.GetRotationGrf(); + + rSize = rRotation.GetUnrotatedSize(); + nRetval = rRotation.GetValue(); + } + } + + return nRetval; +} + +Degree100 SwVirtFlyDrawObj::GetRotateAngle() const +{ + if(ContainsSwGrfNode()) + { + Size aSize; + return to<Degree100>(getPossibleRotationFromFraphicFrame(aSize)); + } + else + { + return SdrVirtObj::GetRotateAngle(); + } +} + +SdrObjectUniquePtr SwVirtFlyDrawObj::getFullDragClone() const +{ + // call parent + SdrObjectUniquePtr pRetval = SdrVirtObj::getFullDragClone(); + + if(pRetval && GetFlyFrame() && ContainsSwGrfNode()) + { + // RotGrfFlyFrame3: get inner bounds/transformation + const basegfx::B2DHomMatrix aTargetTransform(GetFlyFrame()->getFramePrintAreaTransformation()); + + pRetval->TRSetBaseGeometry(aTargetTransform, basegfx::B2DPolyPolygon()); + } + + return pRetval; +} + +void SwVirtFlyDrawObj::addCropHandles(SdrHdlList& rTarget) const +{ + // RotGrfFlyFrame: Adapt to possible rotated Graphic contained in FlyFrame + if(!GetFlyFrame()->getFrameArea().HasArea()) + return; + + // Use InnerBound, OuterBound (same as GetFlyFrame()->getFrameArea().SVRect()) + // may have a distance to InnerBound which needs to be taken into account. + // The Graphic is mapped to InnerBound, as is the rotated Graphic. + const basegfx::B2DRange aTargetRange(getInnerBound()); + + if(aTargetRange.isEmpty()) + return; + + // RotGrfFlyFrame3: get inner bounds/transformation + const basegfx::B2DHomMatrix aTargetTransform(GetFlyFrame()->getFramePrintAreaTransformation()); + + // break up matrix + basegfx::B2DTuple aScale; + basegfx::B2DTuple aTranslate; + double fRotate(0.0); + double fShearX(0.0); + aTargetTransform.decompose(aScale, aTranslate, fRotate, fShearX); + basegfx::B2DPoint aPos; + + aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.0); + rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperLeft, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(0.5, 0.0); + rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Upper, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.0); + rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperRight, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.5); + rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Left , fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.5); + rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Right, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(0.0, 1.0); + rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerLeft, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(0.5, 1.0); + rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Lower, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(1.0, 1.0); + rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerRight, fShearX, fRotate)); +} + +// Macro + +PointerStyle SwVirtFlyDrawObj::GetMacroPointer( + const SdrObjMacroHitRec& ) const +{ + return PointerStyle::RefHand; +} + +bool SwVirtFlyDrawObj::HasMacro() const +{ + const SwFormatURL &rURL = m_pFlyFrame->GetFormat()->GetURL(); + return rURL.GetMap() || !rURL.GetURL().isEmpty(); +} + +SdrObject* SwVirtFlyDrawObj::CheckMacroHit( const SdrObjMacroHitRec& rRec ) const +{ + const SwFormatURL &rURL = m_pFlyFrame->GetFormat()->GetURL(); + if( rURL.GetMap() || !rURL.GetURL().isEmpty() ) + { + SwRect aRect; + if ( m_pFlyFrame->Lower() && m_pFlyFrame->Lower()->IsNoTextFrame() ) + { + aRect = m_pFlyFrame->getFramePrintArea(); + aRect += m_pFlyFrame->getFrameArea().Pos(); + } + else + aRect = m_pFlyFrame->getFrameArea(); + + if( aRect.Contains( rRec.aPos ) ) + { + aRect.Pos().setX(aRect.Pos().getX() + rRec.nTol); + aRect.Pos().setY(aRect.Pos().getY() + rRec.nTol); + aRect.AddHeight( -(2 * rRec.nTol) ); + aRect.AddWidth( -(2 * rRec.nTol) ); + + if( aRect.Contains( rRec.aPos ) ) + { + if( !rURL.GetMap() || + m_pFlyFrame->GetFormat()->GetIMapObject( rRec.aPos, m_pFlyFrame )) + return const_cast<SwVirtFlyDrawObj*>(this); + + return nullptr; + } + } + } + return SdrObject::CheckMacroHit( rRec ); +} + +bool SwVirtFlyDrawObj::IsTextBox() const +{ + return SwTextBoxHelper::isTextBox(GetFormat(), RES_FLYFRMFMT, this); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/draw/dobjfac.cxx b/sw/source/core/draw/dobjfac.cxx new file mode 100644 index 000000000..818b870da --- /dev/null +++ b/sw/source/core/draw/dobjfac.cxx @@ -0,0 +1,39 @@ +/* -*- 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 <dobjfac.hxx> +#include <dflyobj.hxx> +#include <osl/diagnose.h> + +SwObjectFactory aSwObjectFactory; + +IMPL_STATIC_LINK( + SwObjectFactory, MakeObject, SdrObjCreatorParams, aParams, SdrObject* ) +{ + if ( aParams.nInventor == SdrInventor::Swg ) + { + // No switch, there's only one at the moment + OSL_ENSURE( aParams.nObjIdentifier == SdrObjKind::SwFlyDrawObjIdentifier, + "Wrong inventor or identifier" ); + return new SwFlyDrawObj(aParams.rSdrModel); + } + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/draw/dpage.cxx b/sw/source/core/draw/dpage.cxx new file mode 100644 index 000000000..fc1c1ebb7 --- /dev/null +++ b/sw/source/core/draw/dpage.cxx @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <editeng/flditem.hxx> +#include <vcl/imapobj.hxx> +#include <svl/urihelper.hxx> +#include <sfx2/sfxhelp.hxx> +#include <vcl/help.hxx> +#include <svx/svdview.hxx> +#include <osl/diagnose.h> +#include <fmturl.hxx> +#include <frmfmt.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <viewimp.hxx> +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <viewsh.hxx> +#include <drawdoc.hxx> +#include <dpage.hxx> +#include <dcontact.hxx> +#include <dflyobj.hxx> +#include <docsh.hxx> +#include <flyfrm.hxx> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/frame/XModel.hpp> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::frame; + +SwDPage::SwDPage(SwDrawModel& rNewModel, bool bMasterPage) +: FmFormPage(rNewModel, bMasterPage), + m_pDoc(&rNewModel.GetDoc()) +{ +} + +SwDPage::~SwDPage() +{ +} + +void SwDPage::lateInit(const SwDPage& rSrcPage) +{ + FmFormPage::lateInit( rSrcPage ); + + if ( rSrcPage.m_pGridLst ) + { + m_pGridLst.reset( new SdrPageGridFrameList ); + for ( sal_uInt16 i = 0; i != rSrcPage.m_pGridLst->GetCount(); ++i ) + m_pGridLst->Insert( ( *rSrcPage.m_pGridLst )[ i ] ); + } +} + +rtl::Reference<SdrPage> SwDPage::CloneSdrPage(SdrModel& rTargetModel) const +{ + SwDrawModel& rSwDrawModel(static_cast< SwDrawModel& >(rTargetModel)); + rtl::Reference<SwDPage> pClonedSwDPage( + new SwDPage( + rSwDrawModel, + IsMasterPage())); + pClonedSwDPage->lateInit(*this); + return pClonedSwDPage; +} + +SdrObject* SwDPage::ReplaceObject( SdrObject* pNewObj, size_t nObjNum ) +{ + SdrObject *pOld = GetObj( nObjNum ); + OSL_ENSURE( pOld, "Oups, Object not replaced" ); + SdrObjUserCall* pContact; + if ( nullptr != ( pContact = GetUserCall(pOld) ) && + RES_DRAWFRMFMT == static_cast<SwContact*>(pContact)->GetFormat()->Which()) + static_cast<SwDrawContact*>(pContact)->ChangeMasterObject( pNewObj ); + return FmFormPage::ReplaceObject( pNewObj, nObjNum ); +} + +static void InsertGridFrame( SdrPageGridFrameList *pLst, const SwFrame *pPg ) +{ + SwRect aPrt( pPg->getFramePrintArea() ); + aPrt += pPg->getFrameArea().Pos(); + const tools::Rectangle aUser( aPrt.SVRect() ); + const tools::Rectangle aPaper( pPg->getFrameArea().SVRect() ); + pLst->Insert( SdrPageGridFrame( aPaper, aUser ) ); +} + +const SdrPageGridFrameList* SwDPage::GetGridFrameList( + const SdrPageView* pPV, const tools::Rectangle *pRect ) const +{ + SwViewShell* pSh = static_cast< SwDrawModel& >(getSdrModelFromSdrPage()).GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell(); + if(pSh) + { + for(SwViewShell& rShell : pSh->GetRingContainer()) + { + if(rShell.Imp()->GetPageView() == pPV) + { + pSh = &rShell; + break; + } + } + if ( m_pGridLst ) + const_cast<SwDPage*>(this)->m_pGridLst->Clear(); + else + const_cast<SwDPage*>(this)->m_pGridLst.reset( new SdrPageGridFrameList ); + + if ( pRect ) + { + //The drawing demands all pages which overlap with the rest. + const SwRect aRect( *pRect ); + const SwFrame *pPg = pSh->GetLayout()->Lower(); + do + { if ( pPg->getFrameArea().Overlaps( aRect ) ) + ::InsertGridFrame( const_cast<SwDPage*>(this)->m_pGridLst.get(), pPg ); + pPg = pPg->GetNext(); + } while ( pPg ); + } + else + { + //The drawing demands all visible pages + const SwFrame *pPg = pSh->Imp()->GetFirstVisPage(pSh->GetOut()); + if ( pPg ) + do + { ::InsertGridFrame( const_cast<SwDPage*>(this)->m_pGridLst.get(), pPg ); + pPg = pPg->GetNext(); + } while ( pPg && pPg->getFrameArea().Overlaps( pSh->VisArea() ) ); + } + } + return m_pGridLst.get(); +} + +bool SwDPage::RequestHelp( vcl::Window* pWindow, SdrView const * pView, + const HelpEvent& rEvt ) +{ + assert( m_pDoc ); + + bool bContinue = true; + + if( rEvt.GetMode() & ( HelpEventMode::QUICK | HelpEventMode::BALLOON )) + { + Point aPos( rEvt.GetMousePosPixel() ); + aPos = pWindow->ScreenToOutputPixel( aPos ); + aPos = pWindow->PixelToLogic( aPos ); + + SdrPageView* pPV; + SdrObject* pObj = pView->PickObj(aPos, 0, pPV, SdrSearchOptions::PICKMACRO); + SwVirtFlyDrawObj* pDrawObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj); + OUString sText; + tools::Rectangle aPixRect; + bool bTooltip = false; + if (pDrawObj) + { + SwFlyFrame *pFly = pDrawObj->GetFlyFrame(); + + aPixRect = pWindow->LogicToPixel(pFly->getFrameArea().SVRect()); + + const SwFormatURL &rURL = pFly->GetFormat()->GetURL(); + if (!pFly->GetFormat()->GetObjTooltip().isEmpty()) + { + // Tooltips have priority over URLs. + sText = pFly->GetFormat()->GetObjTooltip(); + bTooltip = true; + } + else if( rURL.GetMap() ) + { + IMapObject *pTmpObj = pFly->GetFormat()->GetIMapObject( aPos, pFly ); + if( pTmpObj ) + { + sText = pTmpObj->GetAltText(); + if ( sText.isEmpty() ) + sText = URIHelper::removePassword( pTmpObj->GetURL(), + INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous); + } + } + else if ( !rURL.GetURL().isEmpty() ) + { + sText = URIHelper::removePassword( rURL.GetURL(), + INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous); + + if( rURL.IsServerMap() ) + { + // then append the relative pixel position!! + Point aPt( aPos ); + aPt -= pFly->getFrameArea().Pos(); + // without MapMode-Offset !!!!! + // without MapMode-Offset, without Offset, w ... !!!!! + aPt = pWindow->LogicToPixel( + aPt, MapMode( MapUnit::MapTwip ) ); + sText += "?" + OUString::number( aPt.getX() ) + + "," + OUString::number( aPt.getY() ); + } + } + } + else + { + SdrViewEvent aVEvt; + MouseEvent aMEvt(pWindow->ScreenToOutputPixel(rEvt.GetMousePosPixel()), 1, + MouseEventModifiers::NONE, MOUSE_LEFT); + pView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + if (aVEvt.meEvent == SdrEventKind::ExecuteUrl) + { + sText = aVEvt.mpURLField->GetURL(); + aPixRect = pWindow->LogicToPixel(aVEvt.mpObj->GetLogicRect()); + } + } + + if (!sText.isEmpty()) + { + // #i80029# + bool bExecHyperlinks = m_pDoc->GetDocShell()->IsReadOnly(); + if (!bExecHyperlinks && !bTooltip) + sText = SfxHelp::GetURLHelpText(sText); + + // then display the help: + tools::Rectangle aScreenRect(pWindow->OutputToScreenPixel(aPixRect.TopLeft()), + pWindow->OutputToScreenPixel(aPixRect.BottomRight())); + + if (rEvt.GetMode() & HelpEventMode::BALLOON) + Help::ShowBalloon(pWindow, rEvt.GetMousePosPixel(), aScreenRect, sText); + else + Help::ShowQuickHelp(pWindow, aScreenRect, sText); + bContinue = false; + } + } + + if( bContinue ) + bContinue = !FmFormPage::RequestHelp( pWindow, pView, rEvt ); + + return bContinue; +} + +Reference< XInterface > SwDPage::createUnoPage() +{ + assert( m_pDoc ); + + Reference < XInterface > xRet; + SwDocShell* pDocShell = m_pDoc->GetDocShell(); + if ( pDocShell ) + { + Reference<XModel> xModel = pDocShell->GetBaseModel(); + Reference<XDrawPageSupplier> xPageSupp(xModel, UNO_QUERY); + xRet = xPageSupp->getDrawPage(); + } + return xRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/draw/drawdoc.cxx b/sw/source/core/draw/drawdoc.cxx new file mode 100644 index 000000000..4c3050ebf --- /dev/null +++ b/sw/source/core/draw/drawdoc.cxx @@ -0,0 +1,137 @@ +/* -*- 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 <svx/svxids.hrc> + +#include <com/sun/star/frame/XModel.hpp> +#include <svx/drawitem.hxx> +#include <osl/diagnose.h> +#include <doc.hxx> +#include <drawdoc.hxx> +#include <dpage.hxx> +#include <docsh.hxx> +#include <hintids.hxx> +#include <DocumentSettingManager.hxx> + +using namespace com::sun::star; + +// Constructor +SwDrawModel::SwDrawModel(SwDoc& rDoc) + : FmFormModel(&rDoc.GetAttrPool(), rDoc.GetDocShell()) + , m_rDoc(rDoc) +{ + SetScaleUnit( MapUnit::MapTwip ); + SetSwapGraphics(); + + // use common InitDrawModelAndDocShell which will set the associations as needed, + // including SvxColorTableItem with WhichID SID_COLOR_TABLE + InitDrawModelAndDocShell(m_rDoc.GetDocShell(), this); + + // copy all the default values to the SdrModel + SfxItemPool* pSdrPool = m_rDoc.GetAttrPool().GetSecondaryPool(); + if( pSdrPool ) + { + const sal_uInt16 aWhichRanges[] = + { + RES_CHRATR_BEGIN, RES_CHRATR_END, + RES_PARATR_BEGIN, RES_PARATR_END, + 0 + }; + + SfxItemPool& rDocPool = m_rDoc.GetAttrPool(); + sal_uInt16 nEdtWhich, nSlotId; + const SfxPoolItem* pItem; + for( const sal_uInt16* pRangeArr = aWhichRanges; + *pRangeArr; pRangeArr += 2 ) + for( sal_uInt16 nW = *pRangeArr, nEnd = *(pRangeArr+1); + nW < nEnd; ++nW ) + if( nullptr != (pItem = rDocPool.GetPoolDefaultItem( nW )) && + 0 != (nSlotId = rDocPool.GetSlotId( nW ) ) && + nSlotId != nW && + 0 != (nEdtWhich = pSdrPool->GetWhich( nSlotId )) && + nSlotId != nEdtWhich ) + { + std::unique_ptr<SfxPoolItem> pCpy(pItem->Clone()); + pCpy->SetWhich( nEdtWhich ); + pSdrPool->SetPoolDefaultItem( *pCpy ); + } + } + + SetForbiddenCharsTable(m_rDoc.GetDocumentSettingManager().getForbiddenCharacterTable()); + // Implementation for asian compression + SetCharCompressType( m_rDoc.GetDocumentSettingManager().getCharacterCompressionType() ); +} + +// Destructor + +SwDrawModel::~SwDrawModel() +{ + Broadcast(SdrHint(SdrHintKind::ModelCleared)); + + ClearModel(true); +} + +/** Create a new page (SdPage) and return a pointer to it back. + * + * The drawing engine is using this method while loading for the creating of + * pages (whose type it not even know, because they are inherited from SdrPage). + * + * @return Pointer to the new page. + */ +rtl::Reference<SdrPage> SwDrawModel::AllocPage(bool bMasterPage) +{ + rtl::Reference<SwDPage> pPage = new SwDPage(*this, bMasterPage); + pPage->SetName("Controls"); + return pPage; +} + +uno::Reference<embed::XStorage> SwDrawModel::GetDocumentStorage() const +{ + return m_rDoc.GetDocStorage(); +} + +uno::Reference< uno::XInterface > SwDrawModel::createUnoModel() +{ + uno::Reference< uno::XInterface > xModel; + + try + { + if ( GetDoc().GetDocShell() ) + { + xModel = GetDoc().GetDocShell()->GetModel(); + } + } + catch( uno::RuntimeException& ) + { + OSL_FAIL( "<SwDrawModel::createUnoModel()> - could *not* retrieve model at <SwDocShell>" ); + } + + return xModel; +} + +void SwDrawModel::PutAreaListItems(SfxItemSet& rSet) const +{ + rSet.Put(SvxColorListItem(GetColorList(), SID_COLOR_TABLE)); + rSet.Put(SvxGradientListItem(GetGradientList(), SID_GRADIENT_LIST)); + rSet.Put(SvxHatchListItem(GetHatchList(), SID_HATCH_LIST)); + rSet.Put(SvxBitmapListItem(GetBitmapList(), SID_BITMAP_LIST)); + rSet.Put(SvxPatternListItem(GetPatternList(), SID_PATTERN_LIST)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/draw/dview.cxx b/sw/source/core/draw/dview.cxx new file mode 100644 index 000000000..93b787c91 --- /dev/null +++ b/sw/source/core/draw/dview.cxx @@ -0,0 +1,1019 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> + +#include <hintids.hxx> +#include <svtools/optionsdrawinglayer.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdpagv.hxx> +#include <svx/fmmodel.hxx> +#include <sot/exchange.hxx> +#include <svx/sdrundomanager.hxx> +#include <tools/globname.hxx> +#include <editeng/outliner.hxx> +#include <osl/diagnose.h> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <cntfrm.hxx> +#include <notxtfrm.hxx> +#include <flyfrm.hxx> +#include <frmfmt.hxx> +#include <dflyobj.hxx> +#include <dcontact.hxx> +#include <textboxhelper.hxx> +#include <viewsh.hxx> +#include <viewimp.hxx> +#include <dview.hxx> +#include <doc.hxx> +#include <mdiexp.hxx> +#include <ndole.hxx> +#include <ndgrf.hxx> +#include <fmtanchr.hxx> +#include <IDocumentUndoRedo.hxx> +#include <DocumentSettingManager.hxx> +#include <IDocumentLayoutAccess.hxx> + +#include <com/sun/star/embed/Aspects.hpp> + +#include <vector> + +#include <sortedobjs.hxx> + +using namespace com::sun::star; + +namespace { + +class SwSdrHdl : public SdrHdl +{ +public: + SwSdrHdl(const Point& rPnt, bool bTopRight ) : + SdrHdl( rPnt, bTopRight ? SdrHdlKind::Anchor_TR : SdrHdlKind::Anchor ) {} + virtual bool IsFocusHdl() const override; +}; + +} + +bool SwSdrHdl::IsFocusHdl() const +{ + if( SdrHdlKind::Anchor == eKind || SdrHdlKind::Anchor_TR == eKind ) + return true; + return SdrHdl::IsFocusHdl(); +} + +static const SwFrame *lcl_FindAnchor( const SdrObject *pObj, bool bAll ) +{ + const SwVirtFlyDrawObj *pVirt = dynamic_cast< const SwVirtFlyDrawObj *>( pObj ); + if ( pVirt ) + { + if ( bAll || !pVirt->GetFlyFrame()->IsFlyInContentFrame() ) + return pVirt->GetFlyFrame()->GetAnchorFrame(); + } + else + { + const SwDrawContact *pCont = static_cast<const SwDrawContact*>(GetUserCall(pObj)); + if ( pCont ) + return pCont->GetAnchorFrame( pObj ); + } + return nullptr; +} + +SwDrawView::SwDrawView( + SwViewShellImp& rI, + FmFormModel& rFmFormModel, + OutputDevice* pOutDev) +: FmFormView(rFmFormModel, pOutDev), + m_rImp( rI ) +{ + SetPageVisible( false ); + SetBordVisible( false ); + SetGridVisible( false ); + SetHlplVisible( false ); + SetGlueVisible( false ); + SetFrameDragSingles(); + SetSwapAsynchron(); + + EnableExtendedKeyInputDispatcher( false ); + EnableExtendedMouseEventDispatcher( false ); + + SetHitTolerancePixel( GetMarkHdlSizePixel()/2 ); + + SetPrintPreview( rI.GetShell()->IsPreview() ); + + // #i73602# Use default from the configuration + SetBufferedOverlayAllowed(SvtOptionsDrawinglayer::IsOverlayBuffer_Writer()); + + // #i74769#, #i75172# Use default from the configuration + SetBufferedOutputAllowed(SvtOptionsDrawinglayer::IsPaintBuffer_Writer()); +} + +// #i99665# +bool SwDrawView::IsAntiAliasing() +{ + return SvtOptionsDrawinglayer::IsAntiAliasing(); +} + +static SdrObject* impLocalHitCorrection(SdrObject* pRetval, const Point& rPnt, sal_uInt16 nTol, const SdrMarkList &rMrkList) +{ + if(!nTol) + { + // the old method forced back to outer bounds test when nTol == 0, so + // do not try to correct when nTol is not set (used from HelpContent) + } + else + { + // rebuild logic from former SwVirtFlyDrawObj::CheckSdrObjectHit. This is needed since + // the SdrObject-specific CheckHit implementations are now replaced with primitives and + // 'tricks' like in the old implementation (e.g. using a view from a model-data class to + // detect if object is selected) are no longer valid. + // The standard primitive hit-test for SwVirtFlyDrawObj now is the outer bound. The old + // implementation reduced this excluding the inner bound when the object was not selected. + SwVirtFlyDrawObj* pSwVirtFlyDrawObj = dynamic_cast< SwVirtFlyDrawObj* >(pRetval); + + if(pSwVirtFlyDrawObj) + { + if(pSwVirtFlyDrawObj->GetFlyFrame()->Lower() && pSwVirtFlyDrawObj->GetFlyFrame()->Lower()->IsNoTextFrame()) + { + // the old method used IsNoTextFrame (should be for SW's own OLE and + // graphic's) to accept hit only based on outer bounds; nothing to do + } + else + { + // check if the object is selected in this view + const size_t nMarkCount(rMrkList.GetMarkCount()); + bool bSelected(false); + + for(size_t a = 0; !bSelected && a < nMarkCount; ++a) + { + if(pSwVirtFlyDrawObj == rMrkList.GetMark(a)->GetMarkedSdrObj()) + { + bSelected = true; + } + } + + if(!bSelected) + { + // when not selected, the object is not hit when hit position is inside + // inner range. Get and shrink inner range + basegfx::B2DRange aInnerBound(pSwVirtFlyDrawObj->getInnerBound()); + + aInnerBound.grow(-1.0 * nTol); + + if(aInnerBound.isInside(basegfx::B2DPoint(rPnt.X(), rPnt.Y()))) + { + // exclude this hit + pRetval = nullptr; + } + } + } + } + } + + return pRetval; +} + +SdrObject* SwDrawView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObject* pObj, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay) const +{ + // call parent + SdrObject* pRetval = FmFormView::CheckSingleSdrObjectHit(rPnt, nTol, pObj, pPV, nOptions, pMVisLay); + + if(pRetval) + { + // override to allow extra handling when picking SwVirtFlyDrawObj's + pRetval = impLocalHitCorrection(pRetval, rPnt, nTol, GetMarkedObjectList()); + } + + return pRetval; +} + +/// Gets called every time the handles need to be build +void SwDrawView::AddCustomHdl() +{ + const SdrMarkList &rMrkList = GetMarkedObjectList(); + + if(rMrkList.GetMarkCount() != 1 || !GetUserCall(rMrkList.GetMark( 0 )->GetMarkedSdrObj())) + return; + + SdrObject *pObj = rMrkList.GetMark(0)->GetMarkedSdrObj(); + // make code robust + SwFrameFormat* pFrameFormat( ::FindFrameFormat( pObj ) ); + if ( !pFrameFormat ) + { + OSL_FAIL( "<SwDrawView::AddCustomHdl()> - missing frame format!" ); + return; + } + const SwFormatAnchor &rAnchor = pFrameFormat->GetAnchor(); + + if (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) + return; + + const SwFrame* pAnch = CalcAnchor(); + if(nullptr == pAnch) + return; + + Point aPos(m_aAnchorPoint); + + if ( RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId() ) + { + // #i28701# - use last character rectangle saved at object + // in order to avoid a format of the anchor frame + SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj ); + + // Invalidate/recalc LastCharRect which can contain invalid frame offset because + // of later frame changes + pAnchoredObj->CheckCharRectAndTopOfLine(false); + + SwRect aAutoPos = pAnchoredObj->GetLastCharRect(); + if ( aAutoPos.Height() ) + { + aPos = aAutoPos.Pos(); + } + } + + // add anchor handle: + std::unique_ptr<SdrHdl> hdl = std::make_unique<SwSdrHdl>( aPos, ( pAnch->IsVertical() && !pAnch->IsVertLR() ) || + pAnch->IsRightToLeft() ); + hdl->SetObjHdlNum(maHdlList.GetHdlCount()); + maHdlList.AddHdl(std::move(hdl)); +} + +SdrObject* SwDrawView::GetMaxToTopObj( SdrObject* pObj ) const +{ + if ( GetUserCall(pObj) ) + { + const SwFrame *pAnch = ::lcl_FindAnchor( pObj, false ); + if ( pAnch ) + { + //The topmost Obj within the anchor must not be overtaken. + const SwFlyFrame *pFly = pAnch->FindFlyFrame(); + if ( pFly ) + { + const SwPageFrame *pPage = pFly->FindPageFrame(); + if ( pPage->GetSortedObjs() ) + { + size_t nOrdNum = 0; + for (SwAnchoredObject* i : *pPage->GetSortedObjs()) + { + const SdrObject *pO = i->GetDrawObj(); + + if ( pO->GetOrdNumDirect() > nOrdNum ) + { + const SwFrame *pTmpAnch = ::lcl_FindAnchor( pO, false ); + if ( pFly->IsAnLower( pTmpAnch ) ) + { + nOrdNum = pO->GetOrdNumDirect(); + } + } + } + if ( nOrdNum ) + { + SdrPage *pTmpPage = GetModel()->GetPage( 0 ); + ++nOrdNum; + if ( nOrdNum < pTmpPage->GetObjCount() ) + { + return pTmpPage->GetObj( nOrdNum ); + } + } + } + } + } + } + return nullptr; +} + +SdrObject* SwDrawView::GetMaxToBtmObj(SdrObject* pObj) const +{ + if ( GetUserCall(pObj) ) + { + const SwFrame *pAnch = ::lcl_FindAnchor( pObj, false ); + if ( pAnch ) + { + //The Fly of the anchor must not be "flying under". + const SwFlyFrame *pFly = pAnch->FindFlyFrame(); + if ( pFly ) + { + SdrObject *pRet = const_cast<SdrObject*>(static_cast<SdrObject const *>(pFly->GetVirtDrawObj())); + return pRet != pObj ? pRet : nullptr; + } + } + } + return nullptr; +} + +/// determine maximal order number for a 'child' object of given 'parent' object +sal_uInt32 SwDrawView::GetMaxChildOrdNum( const SwFlyFrame& _rParentObj, + const SdrObject* _pExclChildObj ) +{ + sal_uInt32 nMaxChildOrdNum = _rParentObj.GetDrawObj()->GetOrdNum(); + + const SdrPage* pDrawPage = _rParentObj.GetDrawObj()->getSdrPageFromSdrObject(); + OSL_ENSURE( pDrawPage, + "<SwDrawView::GetMaxChildOrdNum(..) - missing drawing page at parent object - crash!" ); + + const size_t nObjCount = pDrawPage->GetObjCount(); + for ( size_t i = nObjCount-1; i > _rParentObj.GetDrawObj()->GetOrdNum() ; --i ) + { + const SdrObject* pObj = pDrawPage->GetObj( i ); + + // Don't consider 'child' object <_pExclChildObj> + if ( pObj == _pExclChildObj ) + { + continue; + } + + if ( pObj->GetOrdNum() > nMaxChildOrdNum && + _rParentObj.IsAnLower( lcl_FindAnchor( pObj, true ) ) ) + { + nMaxChildOrdNum = pObj->GetOrdNum(); + break; + } + } + + return nMaxChildOrdNum; +} + +/// method to move 'repeated' objects of the given moved object to the according level +void SwDrawView::MoveRepeatedObjs( const SwAnchoredObject& _rMovedAnchoredObj, + const std::vector<SdrObject*>& _rMovedChildObjs ) const +{ + // determine 'repeated' objects of already moved object <_rMovedAnchoredObj> + std::vector<SwAnchoredObject*> aAnchoredObjs; + { + const SwContact* pContact = ::GetUserCall( _rMovedAnchoredObj.GetDrawObj() ); + assert(pContact && "SwDrawView::MoveRepeatedObjs(..) - missing contact object -> crash."); + pContact->GetAnchoredObjs( aAnchoredObjs ); + } + + // check, if 'repeated' objects exists. + if ( aAnchoredObjs.size() <= 1 ) + return; + + SdrPage* pDrawPage = GetModel()->GetPage( 0 ); + + // move 'repeated' ones to the same order number as the already moved one. + const size_t nNewPos = _rMovedAnchoredObj.GetDrawObj()->GetOrdNum(); + while ( !aAnchoredObjs.empty() ) + { + SwAnchoredObject* pAnchoredObj = aAnchoredObjs.back(); + if ( pAnchoredObj != &_rMovedAnchoredObj ) + { + pDrawPage->SetObjectOrdNum( pAnchoredObj->GetDrawObj()->GetOrdNum(), + nNewPos ); + pDrawPage->RecalcObjOrdNums(); + // adjustments for accessibility API +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if ( auto pTmpFlyFrame = pAnchoredObj->DynCastFlyFrame() ) + { + m_rImp.DisposeAccessibleFrame( pTmpFlyFrame ); + m_rImp.AddAccessibleFrame( pTmpFlyFrame ); + } + else + { + m_rImp.DisposeAccessibleObj(pAnchoredObj->GetDrawObj(), true); + m_rImp.AddAccessibleObj( pAnchoredObj->GetDrawObj() ); + } +#endif + } + aAnchoredObjs.pop_back(); + } + + // move 'repeated' ones of 'child' objects + for ( SdrObject* pChildObj : _rMovedChildObjs ) + { + { + const SwContact* pContact = ::GetUserCall( pChildObj ); + assert(pContact && "SwDrawView::MoveRepeatedObjs(..) - missing contact object -> crash."); + pContact->GetAnchoredObjs( aAnchoredObjs ); + } + // move 'repeated' ones to the same order number as the already moved one. + const size_t nTmpNewPos = pChildObj->GetOrdNum(); + while ( !aAnchoredObjs.empty() ) + { + SwAnchoredObject* pAnchoredObj = aAnchoredObjs.back(); + if ( pAnchoredObj->GetDrawObj() != pChildObj ) + { + pDrawPage->SetObjectOrdNum( pAnchoredObj->GetDrawObj()->GetOrdNum(), + nTmpNewPos ); + pDrawPage->RecalcObjOrdNums(); + // adjustments for accessibility API +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if ( auto pTmpFlyFrame = pAnchoredObj->DynCastFlyFrame() ) + { + m_rImp.DisposeAccessibleFrame( pTmpFlyFrame ); + m_rImp.AddAccessibleFrame( pTmpFlyFrame ); + } + else + { + m_rImp.DisposeAccessibleObj(pAnchoredObj->GetDrawObj(), true); + m_rImp.AddAccessibleObj( pAnchoredObj->GetDrawObj() ); + } +#endif + } + aAnchoredObjs.pop_back(); + } + } +} + +// --> adjustment and re-factoring of method +void SwDrawView::ObjOrderChanged( SdrObject* pObj, size_t nOldPos, + size_t nNewPos ) +{ + // nothing to do for group members + if ( pObj->getParentSdrObjectFromSdrObject() ) + { + return; + } + + // determine drawing page and assure that the order numbers are correct. + SdrPage* pDrawPage = GetModel()->GetPage( 0 ); + if ( pDrawPage->IsObjOrdNumsDirty() ) + pDrawPage->RecalcObjOrdNums(); + const size_t nObjCount = pDrawPage->GetObjCount(); + + SwAnchoredObject* pMovedAnchoredObj = + ::GetUserCall( pObj )->GetAnchoredObj( pObj ); + const SwFlyFrame* pParentAnchoredObj = + pMovedAnchoredObj->GetAnchorFrame()->FindFlyFrame(); + + const bool bMovedForward = nOldPos < nNewPos; + + // assure for a 'child' object, that it doesn't exceed the limits of its 'parent' + if ( pParentAnchoredObj ) + { + if ( bMovedForward ) + { + const size_t nMaxChildOrdNumWithoutMoved = + GetMaxChildOrdNum( *pParentAnchoredObj, pMovedAnchoredObj->GetDrawObj() ); + if ( nNewPos > nMaxChildOrdNumWithoutMoved+1 ) + { + // set position to the top of the 'child' object group + pDrawPage->SetObjectOrdNum( nNewPos, nMaxChildOrdNumWithoutMoved+1 ); + nNewPos = nMaxChildOrdNumWithoutMoved+1; + } + } + else + { + const size_t nParentOrdNum = pParentAnchoredObj->GetDrawObj()->GetOrdNum(); + if ( nNewPos < nParentOrdNum ) + { + // set position to the bottom of the 'child' object group + pDrawPage->SetObjectOrdNum( nNewPos, nParentOrdNum ); + nNewPos = nParentOrdNum; + } + } + if ( pDrawPage->IsObjOrdNumsDirty() ) + pDrawPage->RecalcObjOrdNums(); + } + + // Assure, that object isn't positioned between 'repeated' ones + if ( ( bMovedForward && nNewPos < nObjCount - 1 ) || + ( !bMovedForward && nNewPos > 0 ) ) + { + const SdrObject* pTmpObj = + pDrawPage->GetObj( bMovedForward ? nNewPos - 1 : nNewPos + 1 ); + if ( pTmpObj ) + { + size_t nTmpNewPos( nNewPos ); + if ( bMovedForward ) + { + // move before the top 'repeated' object + const sal_uInt32 nTmpMaxOrdNum = + ::GetUserCall( pTmpObj )->GetMaxOrdNum(); + if ( nTmpMaxOrdNum > nNewPos ) + nTmpNewPos = nTmpMaxOrdNum; + } + else + { + // move behind the bottom 'repeated' object + const sal_uInt32 nTmpMinOrdNum = + ::GetUserCall( pTmpObj )->GetMinOrdNum(); + if ( nTmpMinOrdNum < nNewPos ) + nTmpNewPos = nTmpMinOrdNum; + } + if ( nTmpNewPos != nNewPos ) + { + pDrawPage->SetObjectOrdNum( nNewPos, nTmpNewPos ); + nNewPos = nTmpNewPos; + pDrawPage->RecalcObjOrdNums(); + } + } + } + + // On move forward, assure that object is moved before its own children. + // Only Writer fly frames can have children. + if ( pMovedAnchoredObj->DynCastFlyFrame() && + bMovedForward && nNewPos < nObjCount - 1 ) + { + sal_uInt32 nMaxChildOrdNum = + GetMaxChildOrdNum( *static_cast<const SwFlyFrame*>(pMovedAnchoredObj) ); + if ( nNewPos < nMaxChildOrdNum ) + { + // determine position before the object before its top 'child' object + const SdrObject* pTmpObj = pDrawPage->GetObj( nMaxChildOrdNum ); + size_t nTmpNewPos = ::GetUserCall( pTmpObj )->GetMaxOrdNum() + 1; + if ( nTmpNewPos >= nObjCount ) + { + --nTmpNewPos; + } + // assure, that determined position isn't between 'repeated' objects + pTmpObj = pDrawPage->GetObj( nTmpNewPos ); + nTmpNewPos = ::GetUserCall( pTmpObj )->GetMaxOrdNum(); + // apply new position + pDrawPage->SetObjectOrdNum( nNewPos, nTmpNewPos ); + nNewPos = nTmpNewPos; + pDrawPage->RecalcObjOrdNums(); + } + } + + // Assure, that object isn't positioned between nested objects + if ( ( bMovedForward && nNewPos < nObjCount - 1 ) || + ( !bMovedForward && nNewPos > 0 ) ) + { + size_t nTmpNewPos( nNewPos ); + const SwFrameFormat* pParentFrameFormat = + pParentAnchoredObj ? &(pParentAnchoredObj->GetFrameFormat()) : nullptr; + const SdrObject* pTmpObj = pDrawPage->GetObj( nNewPos + 1 ); + while ( pTmpObj ) + { + // #i38563# - assure, that anchor frame exists. + // If object is anchored inside an invisible part of the document + // (e.g. page header, whose page style isn't applied, or hidden + // section), no anchor frame exists. + const SwFrame* pTmpAnchorFrame = lcl_FindAnchor( pTmpObj, true ); + const SwFlyFrame* pTmpParentObj = pTmpAnchorFrame + ? pTmpAnchorFrame->FindFlyFrame() : nullptr; + if ( pTmpParentObj && + &(pTmpParentObj->GetFrameFormat()) != pParentFrameFormat ) + { + if ( bMovedForward ) + { + nTmpNewPos = ::GetUserCall( pTmpObj )->GetMaxOrdNum(); + pTmpObj = pDrawPage->GetObj( nTmpNewPos + 1 ); + } + else + { + nTmpNewPos = ::GetUserCall( pTmpParentObj->GetDrawObj() ) + ->GetMinOrdNum(); + pTmpObj = pTmpParentObj->GetDrawObj(); + } + } + else + break; + } + if ( nTmpNewPos != nNewPos ) + { + pDrawPage->SetObjectOrdNum( nNewPos, nTmpNewPos ); + nNewPos = nTmpNewPos; + pDrawPage->RecalcObjOrdNums(); + } + } + + // setup collection of moved 'child' objects to move its 'repeated' objects. + std::vector< SdrObject* > aMovedChildObjs; + + // move 'children' accordingly + if ( auto pFlyFrame = pMovedAnchoredObj->DynCastFlyFrame() ) + { + // adjustments for accessibility API +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + m_rImp.DisposeAccessibleFrame( pFlyFrame ); + m_rImp.AddAccessibleFrame( pFlyFrame ); +#endif + + const sal_uInt32 nChildNewPos = bMovedForward ? nNewPos : nNewPos+1; + size_t i = bMovedForward ? nOldPos : nObjCount-1; + do + { + SdrObject* pTmpObj = pDrawPage->GetObj( i ); + if ( pTmpObj == pObj ) + break; + + // #i38563# - assure, that anchor frame exists. + // If object is anchored inside an invisible part of the document + // (e.g. page header, whose page style isn't applied, or hidden + // section), no anchor frame exists. + const SwFrame* pTmpAnchorFrame = lcl_FindAnchor( pTmpObj, true ); + const SwFlyFrame* pTmpParentObj = pTmpAnchorFrame + ? pTmpAnchorFrame->FindFlyFrame() : nullptr; + if ( pTmpParentObj && + ( ( pTmpParentObj == pFlyFrame ) || + ( pFlyFrame->IsUpperOf( *pTmpParentObj ) ) ) ) + { + // move child object., + pDrawPage->SetObjectOrdNum( i, nChildNewPos ); + pDrawPage->RecalcObjOrdNums(); + // collect 'child' object + aMovedChildObjs.push_back( pTmpObj ); + // adjustments for accessibility API +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if ( auto pFlyDrawObj = dynamic_cast<SwVirtFlyDrawObj *>( pTmpObj ) ) + { + const SwFlyFrame *pTmpFlyFrame = pFlyDrawObj->GetFlyFrame(); + m_rImp.DisposeAccessibleFrame( pTmpFlyFrame ); + m_rImp.AddAccessibleFrame( pTmpFlyFrame ); + } + else + { + m_rImp.DisposeAccessibleObj(pTmpObj, true); + m_rImp.AddAccessibleObj( pTmpObj ); + } +#endif + } + else + { + // adjust loop counter + if ( bMovedForward ) + ++i; + else if (i > 0) + --i; + } + + } while ( ( bMovedForward && i < ( nObjCount - aMovedChildObjs.size() ) ) || + ( !bMovedForward && i > ( nNewPos + aMovedChildObjs.size() ) ) ); + } +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + else + { + // adjustments for accessibility API + m_rImp.DisposeAccessibleObj(pObj, true); + m_rImp.AddAccessibleObj( pObj ); + } +#endif + + MoveRepeatedObjs( *pMovedAnchoredObj, aMovedChildObjs ); +} + +bool SwDrawView::TakeDragLimit( SdrDragMode eMode, + tools::Rectangle& rRect ) const +{ + const SdrMarkList &rMrkList = GetMarkedObjectList(); + bool bRet = false; + if( 1 == rMrkList.GetMarkCount() ) + { + const SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj(); + SwRect aRect; + if( ::CalcClipRect( pObj, aRect, eMode == SdrDragMode::Move ) ) + { + rRect = aRect.SVRect(); + bRet = true; + } + } + return bRet; +} + +const SwFrame* SwDrawView::CalcAnchor() +{ + const SdrMarkList &rMrkList = GetMarkedObjectList(); + if ( rMrkList.GetMarkCount() != 1 ) + return nullptr; + + SdrObject* pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj(); + + //Search for paragraph bound objects, otherwise only the + //current anchor. Search only if we currently drag. + const SwFrame* pAnch; + tools::Rectangle aMyRect; + auto pFlyDrawObj = dynamic_cast<SwVirtFlyDrawObj *>( pObj ); + if ( pFlyDrawObj ) + { + pAnch = pFlyDrawObj->GetFlyFrame()->GetAnchorFrame(); + aMyRect = pFlyDrawObj->GetFlyFrame()->getFrameArea().SVRect(); + } + else + { + SwDrawContact *pC = static_cast<SwDrawContact*>(GetUserCall(pObj)); + // determine correct anchor position for 'virtual' drawing objects. + // #i26791# + pAnch = pC->GetAnchorFrame( pObj ); + if( !pAnch ) + { + pC->ConnectToLayout(); + // determine correct anchor position for 'virtual' drawing objects. + // #i26791# + pAnch = pC->GetAnchorFrame( pObj ); + } + aMyRect = pObj->GetSnapRect(); + } + + const bool bTopRight = pAnch && ( ( pAnch->IsVertical() && + !pAnch->IsVertLR() ) || + pAnch->IsRightToLeft() ); + const Point aMyPt = bTopRight ? aMyRect.TopRight() : aMyRect.TopLeft(); + + Point aPt; + if ( IsAction() ) + { + if ( !TakeDragObjAnchorPos( aPt, bTopRight ) ) + return nullptr; + } + else + { + tools::Rectangle aRect = pObj->GetSnapRect(); + aPt = bTopRight ? aRect.TopRight() : aRect.TopLeft(); + } + + if ( aPt != aMyPt ) + { + if ( pAnch && pAnch->IsContentFrame() ) + { + // allow drawing objects in header/footer, + // but exclude control objects. + bool bBodyOnly = CheckControlLayer( pObj ); + pAnch = ::FindAnchor( static_cast<const SwContentFrame*>(pAnch), aPt, bBodyOnly ); + } + else if ( !pFlyDrawObj ) + { + const SwRect aRect( aPt.getX(), aPt.getY(), 1, 1 ); + + SwDrawContact* pContact = static_cast<SwDrawContact*>(GetUserCall(pObj)); + if ( pContact->GetAnchorFrame( pObj ) && + pContact->GetAnchorFrame( pObj )->IsPageFrame() ) + pAnch = pContact->GetPageFrame(); + else + pAnch = pContact->FindPage( aRect ); + } + } + if( pAnch && !pAnch->IsProtected() ) + m_aAnchorPoint = pAnch->GetFrameAnchorPos( ::HasWrap( pObj ) ); + else + pAnch = nullptr; + return pAnch; +} + +void SwDrawView::ShowDragAnchor() +{ + SdrHdl* pHdl = maHdlList.GetHdl(SdrHdlKind::Anchor); + if ( ! pHdl ) + pHdl = maHdlList.GetHdl(SdrHdlKind::Anchor_TR); + + if(pHdl) + { + CalcAnchor(); + pHdl->SetPos(m_aAnchorPoint); + } +} + +void SwDrawView::MarkListHasChanged() +{ + Imp().GetShell()->DrawSelChanged(); + FmFormView::MarkListHasChanged(); +} + +// #i7672# +void SwDrawView::ModelHasChanged() +{ + // The ModelHasChanged() call in DrawingLayer also updates + // an eventually active text edit view (OutlinerView). This also leads + // to newly setting the background color for that edit view. Thus, + // this method rescues the current background color if an OutlinerView + // exists and re-establishes it then. To be more safe, the OutlinerView + // will be fetched again (maybe textedit has ended). + OutlinerView* pView = GetTextEditOutlinerView(); + Color aBackColor; + bool bColorWasSaved(false); + + if(pView) + { + aBackColor = pView->GetBackgroundColor(); + bColorWasSaved = true; + } + + // call parent + FmFormView::ModelHasChanged(); + + if(bColorWasSaved) + { + pView = GetTextEditOutlinerView(); + + if(pView) + { + pView->SetBackgroundColor(aBackColor); + } + } +} + +void SwDrawView::MakeVisible( const tools::Rectangle &rRect, vcl::Window & ) +{ + OSL_ENSURE( m_rImp.GetShell()->GetWin(), "MakeVisible, unknown Window"); + m_rImp.GetShell()->MakeVisible( SwRect( rRect ) ); +} + +void SwDrawView::CheckPossibilities() +{ + FmFormView::CheckPossibilities(); + + //In addition to the existing flags of the objects themselves, + //which are evaluated by the DrawingEngine, other circumstances + //lead to a protection. + //Objects that are anchored in frames need to be protected + //if the content of the frame is protected. + //OLE-Objects may themselves wish a resize protection (StarMath) + + const SdrMarkList &rMrkList = GetMarkedObjectList(); + bool bProtect = false; + bool bSzProtect = false; + bool bRotate(false); + + for ( size_t i = 0; !bProtect && i < rMrkList.GetMarkCount(); ++i ) + { + const SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + const SwFrame *pFrame = nullptr; + if ( auto pVirtFlyDrawObj = dynamic_cast< const SwVirtFlyDrawObj *>( pObj ) ) + { + const SwFlyFrame *pFly = pVirtFlyDrawObj->GetFlyFrame(); + if ( pFly ) + { + pFrame = pFly->GetAnchorFrame(); + if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() ) + { + const SwNoTextFrame *const pNTF(static_cast<const SwNoTextFrame*>(pFly->Lower())); + const SwOLENode *const pOLENd = pNTF->GetNode()->GetOLENode(); + const SwGrfNode *const pGrfNd = pNTF->GetNode()->GetGrfNode(); + + if ( pOLENd ) + { + const uno::Reference < embed::XEmbeddedObject > xObj = const_cast< SwOLEObj& >(pOLENd->GetOLEObj()).GetOleRef(); + + if ( xObj.is() ) + { + // --> improvement for the future, when more + // than one Writer fly frame can be selected. + + // TODO/LATER: retrieve Aspect - from where?! + bSzProtect |= ( embed::EmbedMisc::EMBED_NEVERRESIZE & xObj->getStatus( embed::Aspects::MSOLE_CONTENT ) ) != 0; + + // #i972: protect position if it is a Math object anchored 'as char' and baseline alignment is activated + SwDoc* pDoc = Imp().GetShell()->GetDoc(); + const bool bProtectMathPos = SotExchange::IsMath( xObj->getClassID() ) + && RndStdIds::FLY_AS_CHAR == pFly->GetFormat()->GetAnchor().GetAnchorId() + && pDoc->GetDocumentSettingManager().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT ); + if (bProtectMathPos) + m_bMoveProtect = true; + } + } + else if(pGrfNd) + { + // RotGrfFlyFrame: GraphicNode allows rotation(s). The loop ew are in stops + // as soon as bMoveProtect is set, but since rotation is valid only with + // a single object selected this makes no difference + bRotate = true; + } + } + } + } + else + { + SwDrawContact *pC = static_cast<SwDrawContact*>(GetUserCall(pObj)); + if ( pC ) + pFrame = pC->GetAnchorFrame( pObj ); + } + if ( pFrame ) + bProtect = pFrame->IsProtected(); //Frames, areas etc. + { + SwFrameFormat* pFrameFormat( ::FindFrameFormat( const_cast<SdrObject*>(pObj) ) ); + if ( !pFrameFormat ) + { + OSL_FAIL( "<SwDrawView::CheckPossibilities()> - missing frame format" ); + bProtect = true; + } + else if ((RndStdIds::FLY_AS_CHAR == pFrameFormat->GetAnchor().GetAnchorId()) && + rMrkList.GetMarkCount() > 1 ) + { + bProtect = true; + } + } + } + m_bMoveProtect |= bProtect; + m_bResizeProtect |= bProtect || bSzProtect; + + // RotGrfFlyFrame: allow rotation when SwGrfNode is selected and not size protected + m_bRotateFreeAllowed |= bRotate && !bProtect; + m_bRotate90Allowed |= m_bRotateFreeAllowed; +} + +/// replace marked <SwDrawVirtObj>-objects by its reference object for delete marked objects. +void SwDrawView::ReplaceMarkedDrawVirtObjs( SdrMarkView& _rMarkView ) +{ + SdrPageView* pDrawPageView = _rMarkView.GetSdrPageView(); + const SdrMarkList& rMarkList = _rMarkView.GetMarkedObjectList(); + + if( !rMarkList.GetMarkCount() ) + return; + + // collect marked objects in a local data structure + std::vector<SdrObject*> aMarkedObjs; + for( size_t i = 0; i < rMarkList.GetMarkCount(); ++i ) + { + SdrObject* pMarkedObj = rMarkList.GetMark( i )->GetMarkedSdrObj(); + aMarkedObjs.push_back( pMarkedObj ); + } + // unmark all objects + _rMarkView.UnmarkAllObj(); + // re-mark objects, but for marked <SwDrawVirtObj>-objects marked its + // reference object. + while ( !aMarkedObjs.empty() ) + { + SdrObject* pMarkObj = aMarkedObjs.back(); + if ( auto pVirtObj = dynamic_cast<SwDrawVirtObj *>( pMarkObj ) ) + { + SdrObject* pRefObj = &(pVirtObj->ReferencedObj()); + if ( !_rMarkView.IsObjMarked( pRefObj ) ) + { + _rMarkView.MarkObj( pRefObj, pDrawPageView ); + } + } + else + { + _rMarkView.MarkObj( pMarkObj, pDrawPageView ); + } + + aMarkedObjs.pop_back(); + } + // sort marked list in order to assure consistent state in drawing layer + _rMarkView.SortMarkedObjects(); +} + +SfxViewShell* SwDrawView::GetSfxViewShell() const +{ + return m_rImp.GetShell()->GetSfxViewShell(); +} + +void SwDrawView::DeleteMarked() +{ + SwDoc* pDoc = Imp().GetShell()->GetDoc(); + SwRootFrame *pTmpRoot = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + if ( pTmpRoot ) + pTmpRoot->StartAllAction(); + pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); + // replace marked <SwDrawVirtObj>-objects by its reference objects. + if (SdrPageView* pDrawPageView = m_rImp.GetPageView()) + { + ReplaceMarkedDrawVirtObjs(pDrawPageView->GetView()); + } + + // Check what textboxes have to be deleted afterwards. + const SdrMarkList& rMarkList = GetMarkedObjectList(); + std::vector<SwFrameFormat*> aTextBoxesToDelete; + for (size_t i = 0; i < rMarkList.GetMarkCount(); ++i) + { + SdrObject *pObject = rMarkList.GetMark(i)->GetMarkedSdrObj(); + SwContact* pContact = GetUserCall(pObject); + SwFrameFormat* pFormat = pContact->GetFormat(); + if (pObject->getChildrenOfSdrObject()) + { + auto pChildTextBoxes = SwTextBoxHelper::CollectTextBoxes(pObject, pFormat); + for (auto& rChildTextBox : pChildTextBoxes) + aTextBoxesToDelete.push_back(rChildTextBox); + } + else + if (SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT)) + aTextBoxesToDelete.push_back(pTextBox); + } + + if ( pDoc->DeleteSelection( *this ) ) + { + FmFormView::DeleteMarked(); + ::FrameNotify( Imp().GetShell(), FLY_DRAG_END ); + } + + // Only delete these now: earlier deletion would clear the mark list as well. + // Delete in reverse order, assuming that the container is sorted by anchor positions. + for (int i = aTextBoxesToDelete.size() - 1; i >= 0; --i) + { + SwFrameFormat*& rpTextBox = aTextBoxesToDelete[i]; + pDoc->getIDocumentLayoutAccess().DelLayoutFormat(rpTextBox); + } + + pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr); + if( pTmpRoot ) + pTmpRoot->EndAllAction(); +} + +// Create a new view-local UndoManager manager for Writer +std::unique_ptr<SdrUndoManager> SwDrawView::createLocalTextUndoManager() +{ + std::unique_ptr<SdrUndoManager> pUndoManager(new SdrUndoManager); + pUndoManager->SetDocShell(SfxObjectShell::Current()); + return pUndoManager; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |