/* -*- 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 .
*/
// 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>
usingnamespace ::com::sun::star;
staticbool 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
*/ virtualvoid 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
}
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);
}
// 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;
// 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());
}
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);
// 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
*/ virtualvoid createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
public: /// basic constructor, used from SdrObject. explicit VCOfSwVirtFlyDrawObj(SwVirtFlyDrawObj& rObj)
: ViewContactOfVirtObj(rObj)
{
}
// 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));
// check if it is a SwFlyDrawObj* if (rReferencedObject.GetObjIdentifier() == SdrObjKind::SwFlyDrawObjIdentifier)
{ const SwFlyFrame* pFlyFrame = GetFlyFrame();
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);
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);
}
// 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;
// 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());
}
// 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(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) );
// 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(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));
// 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
SwFrame* pLower = GetFlyFrame()->Lower(); if ( pLower && pLower->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 ( constauto &rC : rCol.GetColumns() )
{
nMin += rC.GetLeft() + rC.GetRight() + MINFLY;
}
nMin -= MINFLY;
}
aSz.setWidth( std::max( aSz.Width(), nMin ) );
}
//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());
// 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::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());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.