diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /sw/source/core/draw/dflyobj.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/draw/dflyobj.cxx')
-rw-r--r-- | sw/source/core/draw/dflyobj.cxx | 1295 |
1 files changed, 1295 insertions, 0 deletions
diff --git a/sw/source/core/draw/dflyobj.cxx b/sw/source/core/draw/dflyobj.cxx new file mode 100644 index 000000000..f794887fe --- /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 <tools/mapunit.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/baseprimitive2d.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 drawinglayer::primitive2d::Primitive2DContainer createViewIndependentPrimitive2DSequence() const override; + + public: + /// basic constructor, used from SdrObject. + explicit VCOfSwFlyDrawObj(SwFlyDrawObj& rObj) + : ViewContactOfSdrObj(rObj) + { + } + }; + + } + + drawinglayer::primitive2d::Primitive2DContainer VCOfSwFlyDrawObj::createViewIndependentPrimitive2DSequence() const + { + // currently gets not visualized, return empty sequence + return drawinglayer::primitive2d::Primitive2DContainer(); + } + +} // 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; +} + +sal_uInt16 SwFlyDrawObj::GetObjIdentifier() const +{ + return 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) + : BufferedDecompositionPrimitive2D(), + 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()) + { + // 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 + ImplPrimitive2DIDBlock(SwVirtFlyDrawObjPrimitive, 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 drawinglayer::primitive2d::Primitive2DContainer createViewIndependentPrimitive2DSequence() 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 +{ + drawinglayer::primitive2d::Primitive2DContainer VCOfSwVirtFlyDrawObj::createViewIndependentPrimitive2DSequence() const + { + drawinglayer::primitive2d::Primitive2DContainer xRetval; + 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)); + + xRetval = drawinglayer::primitive2d::Primitive2DContainer { xPrimitive }; + } + } + + return xRetval; + } + +} // 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, long nAngle, 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 (!) + nAngle /= 10; + + while(nAngle < 0) + { + nAngle += 3600; + } + + SwWrtShell *pShForAngle = nAngle ? dynamic_cast<SwWrtShell*>(GetFlyFrame()->getRootFrame()->GetCurrShell()) : nullptr; + if (pShForAngle) + { + // RotGrfFlyFrame: Add transformation to placeholder object + Size aSize; + const sal_uInt16 nOldRot(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize)); + SwFlyFrameAttrMgr aMgr(false, pShForAngle, Frmmgr_Type::NONE, nullptr); + + aMgr.SetRotation(nOldRot, (nOldRot + static_cast<sal_uInt16>(nAngle)) % 3600, aSize); + } + } + else + { + // call parent + SdrVirtObj::Rotate(rRef, nAngle, 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(); + bMovProt = rP.IsPosProtected(); + 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() ) + { + mpOutDev->Push(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() ) + { + bool bDrawObject(true); + + if ( !SwFlyFrame::IsPaint( const_cast<SwVirtFlyDrawObj*>(this), pShell ) ) + { + bDrawObject = false; + } + + if ( bDrawObject ) + { + // 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() ) + { + // 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(), GetFlyFrame()->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)->aOutRect = GetFlyFrame()->getFrameArea().SVRect(); + else + const_cast<SwVirtFlyDrawObj*>(this)->aOutRect = tools::Rectangle(); +} + +const tools::Rectangle& SwVirtFlyDrawObj::GetCurrentBoundRect() const +{ + SetRect(); + return 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 aOutRect; +} + +void SwVirtFlyDrawObj::SetSnapRect(const tools::Rectangle& ) +{ + tools::Rectangle aTmp( GetLastBoundRect() ); + SetRect(); + SetChanged(); + BroadcastObjectChange(); + if (pUserCall!=nullptr) + pUserCall->Changed(*this, SdrUserCallType::Resize, aTmp); +} + +void SwVirtFlyDrawObj::NbcSetSnapRect(const tools::Rectangle& ) +{ + SetRect(); +} + +const tools::Rectangle& SwVirtFlyDrawObj::GetLogicRect() const +{ + SetRect(); + return aOutRect; +} + +void SwVirtFlyDrawObj::SetLogicRect(const tools::Rectangle& ) +{ + tools::Rectangle aTmp( GetLastBoundRect() ); + SetRect(); + SetChanged(); + BroadcastObjectChange(); + if (pUserCall!=nullptr) + 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(); + aOutRect = GetFlyFrame()->getFrameArea().SVRect(); + } + + aOutRect.Move( rSiz ); + const Point aOldPos( GetFlyFrame()->getFrameArea().Pos() ); + const Point aNewPos( aOutRect.TopLeft() ); + const SwRect aFlyRect( 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(); + 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; + + 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(); + 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 long nOldWidth(aOldRect.GetWidth()); + const 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 + SfxItemSet aSet( pSh->GetAttrPool(), svl::Items<RES_GRFATR_CROPGRF, RES_GRFATR_CROPGRF>{} ); + 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 (convertMm100ToTwip(nLeftCrop)); + aCrop.SetTop (convertMm100ToTwip(nTopCrop)); + aCrop.SetRight (convertMm100ToTwip(nRightCrop)); + aCrop.SetBottom(convertMm100ToTwip(nBottomCrop)); + pSh->SetAttrItem(aCrop); + + // Set new frame size + SwFrameFormat *pFormat = GetFormat(); + SwFormatFrameSize aSz( pFormat->GetFrameSize() ); + const long aNewWidth(aNewRect.GetWidth() + (aOutRect.GetWidth() - aOldRect.GetWidth())); + const long aNewHeight(aNewRect.GetHeight() + (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 + 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( 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( aOutRect.Right() - aOutRect.Left() + 1, aOutRect.Bottom()- 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(); + 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() ) + { + 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 ? aOutRect.Right() + 1 : aOutRect.Left(), aOutRect.Top()); + + if ( aNewPos != aOldPos ) + { + // 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()); + 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(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 +sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(Size& rSize) const +{ + sal_uInt16 nRetval(0); + 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; +} + +long SwVirtFlyDrawObj::GetRotateAngle() const +{ + if(ContainsSwGrfNode()) + { + Size aSize; + return 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()) + { + // 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()) + { + // 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.IsInside( 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.IsInside( 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); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |