1
0
Fork 0
libreoffice/sw/source/core/draw/dflyobj.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

1335 lines
49 KiB
C++

/* -*- 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;
}
rtl::Reference<SdrObject> SwFlyDrawObj::CloneSdrObject(SdrModel& rTargetModel) const
{
return new SwFlyDrawObj(rTargetModel);
}
void SwFlyDrawObj::NbcRotate(const Point& /*rRef*/, Degree100 /*nAngle*/, double /*sinAngle*/, double /*cosAngle*/)
{
assert(false);
}
// 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 Primitive2DReference create2DDecomposition(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
{
Primitive2DReference SwVirtFlyDrawObjPrimitive::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if(getOuterRange().isEmpty())
return nullptr;
// 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
return
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();
// check if it is a SwFlyDrawObj*
if (rReferencedObject.GetObjIdentifier() == SdrObjKind::SwFlyDrawObjIdentifier)
{
// 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();
// check if it is a SwFlyDrawObj*
if (rReferencedObject.GetObjIdentifier() == SdrObjKind::SwFlyDrawObjIdentifier)
{
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()
{
assert (!getSdrPageFromSdrObject() && "should have already been removed");
}
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
{
auto* pWritableThis = const_cast<SwVirtFlyDrawObj*>(this);
if ( GetFlyFrame()->getFrameArea().HasArea() )
pWritableThis->setOutRectangle(GetFlyFrame()->getFrameArea().SVRect());
else
pWritableThis->resetOutRectangle();
}
const tools::Rectangle& SwVirtFlyDrawObj::GetCurrentBoundRect() const
{
SetRect();
return getOutRectangle();
}
const tools::Rectangle& SwVirtFlyDrawObj::GetLastBoundRect() const
{
return GetCurrentBoundRect();
}
void SwVirtFlyDrawObj::RecalcBoundRect()
{
SetRect();
}
void SwVirtFlyDrawObj::RecalcSnapRect()
{
SetRect();
}
const tools::Rectangle& SwVirtFlyDrawObj::GetSnapRect() const
{
SetRect();
return getOutRectangle();
}
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 getOutRectangle();
}
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&, bool /*bAdaptTextMinSize*/ )
{
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();
setOutRectangle(GetFlyFrame()->getFrameArea().SVRect());
}
moveOutRectangle(rSiz.Width(), rSiz.Height());
const Point aOldPos( GetFlyFrame()->getFrameArea().Pos() );
const Point aNewPos(getOutRectangle().TopLeft());
const SwRect aFlyRect(getOutRectangle());
//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();
setOutRectangle(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<tools::Long>(aOldRect.GetWidth() * rRef.getX()),
aOldRect.Top() + basegfx::fround<tools::Long>(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() + (getOutRectangle().GetWidth() - aOldRect.GetWidth()));
const tools::Long aNewHeight(aNewRect.GetHeight() + (getOutRectangle().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<tools::Long>(aRotNewCenter.getX() - (0.5 * aNewRect.getOpenWidth())),
basegfx::fround<tools::Long>(aRotNewCenter.getY() - (0.5 * aNewRect.getOpenHeight())));
}
// 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();
}
bool SwVirtFlyDrawObj::IsSizeValid(Size aTargetSize)
{
SwBorderAttrAccess aAccess( SwFrame::GetCache(), GetFlyFrame() );
const SwBorderAttrs &rAttrs = *aAccess.Get();
return GetFlyFrame()->IsResizeValid(&rAttrs, aTargetSize);
}
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());
tools::Rectangle aRectangle;
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
setOutRectangle(tools::Rectangle(
basegfx::fround<tools::Long>(aCenter.getX() - (0.5 * aAbsScale.getX())),
basegfx::fround<tools::Long>(aCenter.getY() - (0.5 * aAbsScale.getY())),
basegfx::fround<tools::Long>(aCenter.getX() + (0.5 * aAbsScale.getX())),
basegfx::fround<tools::Long>(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
{
aRectangle = getOutRectangle();
ResizeRect(aRectangle, rRef, xFact, yFact);
setOutRectangle(aRectangle);
}
// 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
aRectangle = getOutRectangle();
Size aSz(aRectangle.Right() - aRectangle.Left() + 1, aRectangle.Bottom() - aRectangle.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
aRectangle = getOutRectangle();
const Point aNewPos(bUseRightEdge ? aRectangle.Right() + 1 : aRectangle.Left(), aRectangle.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());
moveOutRectangle(-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(getOutRectangle()));
}
// 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();
}
}
rtl::Reference<SdrObject> SwVirtFlyDrawObj::getFullDragClone() const
{
// call parent
rtl::Reference<SdrObject> 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<tools::Long>(aPos.getX()), basegfx::fround<tools::Long>(aPos.getY())), SdrHdlKind::UpperLeft, fShearX, fRotate));
aPos = aTargetTransform * basegfx::B2DPoint(0.5, 0.0);
rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround<tools::Long>(aPos.getX()), basegfx::fround<tools::Long>(aPos.getY())), SdrHdlKind::Upper, fShearX, fRotate));
aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.0);
rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround<tools::Long>(aPos.getX()), basegfx::fround<tools::Long>(aPos.getY())), SdrHdlKind::UpperRight, fShearX, fRotate));
aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.5);
rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround<tools::Long>(aPos.getX()), basegfx::fround<tools::Long>(aPos.getY())), SdrHdlKind::Left , fShearX, fRotate));
aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.5);
rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround<tools::Long>(aPos.getX()), basegfx::fround<tools::Long>(aPos.getY())), SdrHdlKind::Right, fShearX, fRotate));
aPos = aTargetTransform * basegfx::B2DPoint(0.0, 1.0);
rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround<tools::Long>(aPos.getX()), basegfx::fround<tools::Long>(aPos.getY())), SdrHdlKind::LowerLeft, fShearX, fRotate));
aPos = aTargetTransform * basegfx::B2DPoint(0.5, 1.0);
rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround<tools::Long>(aPos.getX()), basegfx::fround<tools::Long>(aPos.getY())), SdrHdlKind::Lower, fShearX, fRotate));
aPos = aTargetTransform * basegfx::B2DPoint(1.0, 1.0);
rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround<tools::Long>(aPos.getX()), basegfx::fround<tools::Long>(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);
}
void SwVirtFlyDrawObj::dumpAsXml(xmlTextWriterPtr pWriter) const
{
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwVirtFlyDrawObj"));
(void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
(void)xmlTextWriterWriteAttribute(
pWriter, BAD_CAST("fly-frame"),
BAD_CAST(OString::number(m_pFlyFrame->GetFrameId()).getStr()));
SdrVirtObj::dumpAsXml(pWriter);
(void)xmlTextWriterEndElement(pWriter);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */