[Libreoffice-commits] core.git: Branch 'feature/RotGrfFlyFrame' - basegfx/source include/basegfx svx/source sw/source

Armin Le Grand Armin.Le.Grand at cib.de
Thu Sep 28 08:24:42 UTC 2017


 basegfx/source/matrix/b2dhommatrixtools.cxx  |   42 ++
 include/basegfx/matrix/b2dhommatrixtools.hxx |    7 
 svx/source/svdraw/svddrgmt.cxx               |  443 ++++++++++-----------------
 sw/source/core/doc/notxtfrm.cxx              |   51 ---
 sw/source/core/draw/dflyobj.cxx              |  100 +++++-
 sw/source/core/inc/dflyobj.hxx               |    7 
 sw/source/core/inc/frmtool.hxx               |    7 
 7 files changed, 331 insertions(+), 326 deletions(-)

New commits:
commit 0de6fda489c7d07036edba92491d5798892b845b
Author: Armin Le Grand <Armin.Le.Grand at cib.de>
Date:   Thu Sep 28 10:20:15 2017 +0200

    RotGrfFlyFrame: Adapt Crop functionality to rotated Graphic
    
    The FlyFrame which may contain a Graphic needs working Crop,
    interactive and in core. Adapted this to work with now possible
    rotation, changed common code in svx which has to handle cases
    for Draw/Impress/Calc and Writer differently. Tried to use as
    much in common as possible. Additionally furter adaptions
    to rotation itself.
    
    Change-Id: Ia961e9490e2627c74220b186116f5aa4fcabca78

diff --git a/basegfx/source/matrix/b2dhommatrixtools.cxx b/basegfx/source/matrix/b2dhommatrixtools.cxx
index 89ab91424706..013bb7d23703 100644
--- a/basegfx/source/matrix/b2dhommatrixtools.cxx
+++ b/basegfx/source/matrix/b2dhommatrixtools.cxx
@@ -358,6 +358,48 @@ namespace basegfx
             return aRetval;
         }
 
+        BASEGFX_DLLPUBLIC B2DHomMatrix createRotateAroundCenterKeepAspectRatioStayInsideRange(
+            const basegfx::B2DRange& rTargetRange,
+            double fRotate)
+        {
+            basegfx::B2DHomMatrix aRetval;
+
+            // RotGrfFlyFrame: Take rotation into account. Rotation is in 10th degrees
+            if(0.0 != fRotate)
+            {
+                // Fit rotated graphic to center of available space, keeping page ratio:
+                // Adapt scaling ratio of unit object and rotate it
+                aRetval.scale(1.0, rTargetRange.getHeight() / rTargetRange.getWidth());
+                aRetval.rotate(fRotate);
+
+                // get the range to see where we are in unit coordinates
+                basegfx::B2DRange aFullRange(0.0, 0.0, 1.0, 1.0);
+                aFullRange.transform(aRetval);
+
+                // detect needed scales in X/Y and choose the smallest for staying inside the
+                // available space while keeping aspect ratio of the source
+                const double fScaleX(rTargetRange.getWidth() / aFullRange.getWidth());
+                const double fScaleY(rTargetRange.getHeight() / aFullRange.getHeight());
+                const double fScaleMin(std::min(fScaleX, fScaleY));
+
+                // TopLeft to zero, then scale, then move to center of available space
+                aRetval.translate(-aFullRange.getMinX(), -aFullRange.getMinY());
+                aRetval.scale(fScaleMin, fScaleMin);
+                aRetval.translate(
+                    rTargetRange.getCenterX() - (0.5 * fScaleMin * aFullRange.getWidth()),
+                    rTargetRange.getCenterY() - (0.5 * fScaleMin * aFullRange.getHeight()));
+            }
+            else
+            {
+                // just scale/translate needed
+                aRetval *= createScaleTranslateB2DHomMatrix(
+                    rTargetRange.getRange(),
+                    rTargetRange.getMinimum());
+            }
+
+            return aRetval;
+        }
+
         /// special for the case to map from source range to target range
         B2DHomMatrix createSourceRangeTargetRangeTransform(
             const B2DRange& rSourceRange,
diff --git a/include/basegfx/matrix/b2dhommatrixtools.hxx b/include/basegfx/matrix/b2dhommatrixtools.hxx
index aa3c047a20c5..1a2706cd1378 100644
--- a/include/basegfx/matrix/b2dhommatrixtools.hxx
+++ b/include/basegfx/matrix/b2dhommatrixtools.hxx
@@ -126,6 +126,13 @@ namespace basegfx
                 fRadiant);
         }
 
+        /// special for creating a mapping for a Range rotated around it's center
+        /// while keeping AspectRatio unchanged and staying inside the given Range
+        /// by optimally using the available space (no overlap or outside allowed)
+        BASEGFX_DLLPUBLIC B2DHomMatrix createRotateAroundCenterKeepAspectRatioStayInsideRange(
+            const basegfx::B2DRange& rTargetRange,
+            double fRotate);
+
         /// special for the case to map from source range to target range
         BASEGFX_DLLPUBLIC B2DHomMatrix createSourceRangeTargetRangeTransform(
             const B2DRange& rSourceRange,
diff --git a/svx/source/svdraw/svddrgmt.cxx b/svx/source/svdraw/svddrgmt.cxx
index ecb7800273f3..67efd4b9da5a 100644
--- a/svx/source/svdraw/svddrgmt.cxx
+++ b/svx/source/svdraw/svddrgmt.cxx
@@ -3585,200 +3585,62 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
 {
     Hide();
 
-    if( DragStat().GetDX()==0 && DragStat().GetDY()==0 )
+    if(0 == DragStat().GetDX() && 0 == DragStat().GetDY())
+    {
+        // no change, done
         return false;
+    }
 
     const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
 
-    if( rMarkList.GetMarkCount() != 1 )
+    if(1 != rMarkList.GetMarkCount())
+    {
+        // Crop only with single Object selected
         return false;
+    }
 
-    SdrObject* pSdrObject = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
-
-    // tdf#34555: in order to implement visual crop in Writer, we need to handle two
-    // cases:
-    // EndSdrDrag when called in Impress/Draw/...: pSdrObject is a SdrGrafObj
-    // EndSdrDrag when called in Writer: pSdrObject is a SwVirtFlyDrawObj
-    // Main principle: if marked object is not SdrGrafObj, we start a generic handling
-    // based on virtual methods added to SdrObject, on MM100/Twip coordinates and so on.
-    // If marked object is SdrGrafObj, we do all the work here with matrix based
-    // coordinates.
-    if (dynamic_cast<const SdrGrafObj*>( pSdrObject) ==  nullptr) {
-        const bool bUndo = getSdrDragView().IsUndoEnabled();
-        if( bUndo )
-        {
-            OUString aUndoStr;
-            ImpTakeDescriptionStr(STR_DragMethCrop, aUndoStr);
-            getSdrDragView().BegUndo( aUndoStr );
-            getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pSdrObject));
-            // also need attr undo, the SdrGrafCropItem will be changed
-            getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pSdrObject));
-        }
-
-        // We need to produce a reference point and two (X & Y) scales
-        SdrHdl* pRef1=GetHdlList().GetHdl(SdrHdlKind::UpperLeft);
-        SdrHdl* pRef2=GetHdlList().GetHdl(SdrHdlKind::LowerRight);
-
-        if (pRef1==nullptr || pRef2==nullptr)
-            return false;
-
-        tools::Rectangle rect(pRef1->GetPos(),pRef2->GetPos());
-
-        Point aEnd(DragStat().GetNow());
-        Point aStart(DragStat().GetStart());
-        Point aRef(rect.Center());
-
-        // Reference point is the point opposed to the dragged handle
-        switch(GetDragHdlKind())
-        {
-            case SdrHdlKind::UpperLeft: aRef = rect.BottomRight();                                  break;
-            case SdrHdlKind::Upper: aRef = rect.BottomCenter(); DragStat().SetHorFixed(true);   break;
-            case SdrHdlKind::UpperRight: aRef = rect.BottomLeft();                                   break;
-            case SdrHdlKind::Left : aRef = rect.RightCenter();  DragStat().SetVerFixed(true);   break;
-            case SdrHdlKind::Right: aRef = rect.LeftCenter();   DragStat().SetVerFixed(true);   break;
-            case SdrHdlKind::LowerLeft: aRef = rect.TopRight();                                     break;
-            case SdrHdlKind::Lower: aRef = rect.TopCenter();    DragStat().SetHorFixed(true);   break;
-            case SdrHdlKind::LowerRight: aRef = rect.TopLeft();                                      break;
-            default: break;
-        }
-
-        // By default, scale is new size / old size
-        long nXDiv = aStart.X()-aRef.X(); if (nXDiv==0) nXDiv=1;
-        long nYDiv = aStart.Y()-aRef.Y(); if (nYDiv==0) nYDiv=1;
-        long nXMul = aEnd.X()-aRef.X();
-        long nYMul = aEnd.Y()-aRef.Y();
-
-        if (nXDiv<0)
-        {
-            nXDiv=-nXDiv;
-            nXMul=-nXMul;
-        }
-
-        if (nYDiv<0)
-        {
-            nYDiv=-nYDiv;
-            nYMul=-nYMul;
-        }
-
-        // Take ortho into account.
-        bool bXNeg=nXMul<0; if (bXNeg) nXMul=-nXMul;
-        bool bYNeg=nYMul<0; if (bYNeg) nYMul=-nYMul;
-        bool bOrtho=getSdrDragView().IsOrtho() || !getSdrDragView().IsResizeAllowed();
-
-        if (!DragStat().IsHorFixed() && !DragStat().IsVerFixed())
-        {
-            if (std::abs(nXDiv)<=1 || std::abs(nYDiv)<=1)
-                bOrtho=false;
-
-            if (bOrtho)
-            {
-                if ((Fraction(nXMul,nXDiv)>Fraction(nYMul,nYDiv)) !=getSdrDragView().IsBigOrtho())
-                {
-                    nXMul=nYMul;
-                    nXDiv=nYDiv;
-                }
-                else
-                {
-                    nYMul=nXMul;
-                    nYDiv=nXDiv;
-                }
-            }
-        }
-        else
-        {
-            if (bOrtho)
-            {
-                if (DragStat().IsHorFixed())
-                {
-                    bXNeg=false;
-                    nXMul=nYMul;
-                    nXDiv=nYDiv;
-                }
-
-                if (DragStat().IsVerFixed())
-                {
-                    bYNeg=false;
-                    nYMul=nXMul;
-                    nYDiv=nXDiv;
-                }
-            }
-            else
-            {
-                if (DragStat().IsHorFixed())
-                {
-                    bXNeg=false;
-                    nXMul=1;
-                    nXDiv=1;
-                }
+    // prepare for SdrGrafObj or others. This code has to work with usual
+    // SdrGrafObj's from Draw/Impress/Calc, but also with SdrObjects from
+    // Writer. It would be better to handle this in Writer directly, but
+    // there are currently no easy mechanisms to plug an alternative interaction
+    // from there
+    SdrObject* pSdrObject = rMarkList.GetMark(0)->GetMarkedSdrObj();
+    struct SdrObjDeleter { void operator()(SdrObject* b) { SdrObject::Free(b); }};
+    std::unique_ptr< SdrObject, SdrObjDeleter > pFullDragClone;
+    bool bExternal(false);
+    SdrObject* pExternalSdrObject(nullptr);
 
-                if (DragStat().IsVerFixed())
-                {
-                    bYNeg=false;
-                    nYMul=1;
-                    nYDiv=1;
-                }
-            }
-        }
-        Fraction aXFact(nXMul,nXDiv);
-        Fraction aYFact(nYMul,nYDiv);
-        Fraction aMaxFact(0x7FFFFFFF,1);
+    // RotGrfFlyFrame: Crop decision for DrawingLayer/Writer now
+    // locally, no two-in-one methods any more
+    if (nullptr != pSdrObject && dynamic_cast< const SdrGrafObj* >(pSdrObject) ==  nullptr)
+    {
+        // If Writer, get the already offered for interaction SdrGrafObj
+        // and set up for using that replacement object that contains the
+        // real transformation. That SdrObject is owned and has to be deleted,
+        // so use a std::unique_ptr with special handling for the protected
+        // SDrObject destructor
+        pFullDragClone.reset(pSdrObject->getFullDragClone());
 
-        if (bOrtho)
+        if(pFullDragClone && dynamic_cast< SdrGrafObj* >(pFullDragClone.get()))
         {
-            if (aXFact>aMaxFact)
-            {
-                aXFact=aMaxFact;
-                aYFact=aMaxFact;
-            }
-
-            if (aYFact>aMaxFact)
-            {
-                aXFact=aMaxFact;
-                aYFact=aMaxFact;
-            }
+            bExternal = true;
+            pExternalSdrObject = pSdrObject;
+            pSdrObject = pFullDragClone.get();
         }
-
-        if (bXNeg)
-            aXFact=Fraction(-aXFact.GetNumerator(),aXFact.GetDenominator());
-
-        if (bYNeg)
-            aYFact=Fraction(-aYFact.GetNumerator(),aYFact.GetDenominator());
-
-        // With Ref point (opposed to dragged point), X scale and Y scale,
-        // we call crop (virtual method) on pSdrObject which calls VirtFlyDrawObj
-        // crop
-        pSdrObject->Crop(aRef, aXFact, aYFact);
-
-        if( bUndo )
-            getSdrDragView().EndUndo();
-
-        // Job's done
-        return true;
     }
 
-    // This part of code handles the case where pSdrObject is SdrGrafObj
-
+    // get and check for SdrGrafObj now
     SdrGrafObj* pObj = dynamic_cast<SdrGrafObj*>( pSdrObject );
-    if( !pObj || (pObj->GetGraphicType() == GraphicType::NONE) || (pObj->GetGraphicType() == GraphicType::Default) )
-        return false;
 
-    const GraphicObject& rGraphicObject = pObj->GetGraphicObject();
-    const MapMode aMapMode100thmm(MapUnit::Map100thMM);
-    Size aGraphicSize(rGraphicObject.GetPrefSize());
-
-    if( MapUnit::MapPixel == rGraphicObject.GetPrefMapMode().GetMapUnit() )
-        aGraphicSize = Application::GetDefaultDevice()->PixelToLogic( aGraphicSize, aMapMode100thmm );
-    else
-        aGraphicSize = OutputDevice::LogicToLogic( aGraphicSize, rGraphicObject.GetPrefMapMode(), aMapMode100thmm);
-
-    if( aGraphicSize.Width() == 0 || aGraphicSize.Height() == 0 )
+    if(!pObj)
+    {
         return false;
+    }
 
-    const SdrGrafCropItem& rOldCrop = static_cast<const SdrGrafCropItem&>(pObj->GetMergedItem(SDRATTR_GRAFCROP));
-
-    const bool bUndo = getSdrDragView().IsUndoEnabled();
+    // no undo for external needed, done there
+    const bool bUndo(!bExternal && getSdrDragView().IsUndoEnabled());
 
-    if( bUndo )
+    if(bUndo)
     {
         OUString aUndoStr;
         ImpTakeDescriptionStr(STR_DragMethCrop, aUndoStr);
@@ -3789,20 +3651,15 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
         getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
     }
 
-    // new part to commute the user's drag activities
     // get the original objects transformation
     basegfx::B2DHomMatrix aOriginalMatrix;
     basegfx::B2DPolyPolygon aPolyPolygon;
     bool bShearCorrected(false);
-
-    // get transformation from object
     pObj->TRGetBaseGeometry(aOriginalMatrix, aPolyPolygon);
 
     {   // correct shear, it comes currently mirrored from TRGetBaseGeometry, can be removed with aw080
-        basegfx::B2DTuple aScale;
-        basegfx::B2DTuple aTranslate;
+        basegfx::B2DTuple aScale, aTranslate;
         double fRotate(0.0), fShearX(0.0);
-
         aOriginalMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
 
         if(!basegfx::fTools::equalZero(fShearX))
@@ -3816,11 +3673,6 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
         }
     }
 
-    // invert it to be able to work on unit coordinates
-    basegfx::B2DHomMatrix aInverse(aOriginalMatrix);
-
-    aInverse.invert();
-
     // generate start point of original drag vector in unit coordinates (the
     // vis-a-vis of the drag point)
     basegfx::B2DPoint aLocalStart(0.0, 0.0);
@@ -3839,7 +3691,10 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
         default: break;
     }
 
-    // create the current drag position in unit coordinates
+    // create the current drag position in unit coordinates. To get there,
+    // transform back the DragPoint to UnitCoordinates
+    basegfx::B2DHomMatrix aInverse(aOriginalMatrix);
+    aInverse.invert();
     basegfx::B2DPoint aLocalCurrent(aInverse * basegfx::B2DPoint(DragStat().GetNow().X(), DragStat().GetNow().Y()));
 
     // if one of the edge handles is used, limit to X or Y drag only
@@ -3884,48 +3739,18 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
         }
     }
 
-    // preparematrix to apply to object; evtl. back-correct shear
-    basegfx::B2DHomMatrix aNewObjectMatrix(aOriginalMatrix * aDiscreteChangeMatrix);
-
-    if(bShearCorrected)
-    {
-        // back-correct shear
-        basegfx::B2DTuple aScale;
-        basegfx::B2DTuple aTranslate;
-        double fRotate(0.0), fShearX(0.0);
-
-        aNewObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
-        aNewObjectMatrix = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
-            aScale,
-            -fShearX,
-            fRotate,
-            aTranslate);
-    }
-
-    // apply change to object by applying the unit coordinate change followed
-    // by the original change
-    pObj->TRSetBaseGeometry(aNewObjectMatrix, aPolyPolygon);
-
-    // the following old code uses aOldRect/aNewRect to calculate the crop change for
-    // the crop item. It implies unrotated objects, so create the unrotated original
-    // rectangle and the unrotated modified rectangle. Latter can in case of shear and/or
-    // rotation not be fetched by using
-
-    //Rectangle aNewRect( pObj->GetLogicRect() );
-
-    // as it was done before because the top-left of that new rect *will* have an offset
-    // caused by the evtl. existing shear and/or rotation, so calculate a unrotated
-    // rectangle how it would be as a result when applying the unit coordinate change
-    // to the unrotated original transformation.
-    basegfx::B2DTuple aScale;
-    basegfx::B2DTuple aTranslate;
+    // We now have the whole executed Crop in UnitCoordinates in
+    // aDiscreteChangeMatrix, go to concrete sizes now.
+    // Create the unrotated original rectangle and the unrotated modified
+    // rectangle as Ranges
+    basegfx::B2DTuple aScale, aTranslate;
     double fRotate, fShearX;
 
     // get access to scale and translate
     aOriginalMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
 
     // prepare unsheared/unrotated versions of the old and new transformation
-    const basegfx::B2DHomMatrix aMatrixOriginalNoShearNoRotate(
+    const basegfx::B2DHomMatrix aOriginalMatrixNoShearNoRotate(
         basegfx::tools::createScaleTranslateB2DHomMatrix(
             basegfx::absolute(aScale),
             aTranslate));
@@ -3933,55 +3758,129 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
     // create the ranges for these
     basegfx::B2DRange aRangeOriginalNoShearNoRotate(0.0, 0.0, 1.0, 1.0);
     basegfx::B2DRange aRangeNewNoShearNoRotate(0.0, 0.0, 1.0, 1.0);
+    aRangeOriginalNoShearNoRotate.transform(aOriginalMatrixNoShearNoRotate);
+    aRangeNewNoShearNoRotate.transform(aOriginalMatrixNoShearNoRotate * aDiscreteChangeMatrix);
+
+    if(bExternal)
+    {
+        // With Ref point (opposed to dragged point), X scale and Y scale,
+        // we call crop (virtual method) on pSdrObject which calls VirtFlyDrawObj
+        // crop. aRef needs to be adapted to concrete Object's boundaries which
+        // is different from Crop-Ranges. This is because the Graphic and it's
+        // SdrObject representaion is inside the FlyFrame, but not identical
+        // with it.
+        const tools::Rectangle& rOutRect(pExternalSdrObject->GetCurrentBoundRect());
+        const basegfx::B2DHomMatrix aExternalTransform(
+            basegfx::tools::createScaleTranslateB2DHomMatrix(
+                rOutRect.getWidth(), rOutRect.getHeight(),
+                rOutRect.Left(), rOutRect.Top()));
+        const basegfx::B2DPoint aRef(aExternalTransform * aLocalStart);
+        const double fScaleX(aRangeNewNoShearNoRotate.getWidth() / aRangeOriginalNoShearNoRotate.getWidth());
+        const double fScaleY(aRangeNewNoShearNoRotate.getHeight() / aRangeOriginalNoShearNoRotate.getHeight());
+
+        pExternalSdrObject->Crop(
+            Point(basegfx::fround(aRef.getX()), basegfx::fround(aRef.getY())),
+            Fraction(fScaleX),
+            Fraction(fScaleY));
+    }
+    else
+    {
+        // prepare matrix to apply to object; evtl. back-correct shear
+        basegfx::B2DHomMatrix aNewObjectMatrix(aOriginalMatrix * aDiscreteChangeMatrix);
+
+        if(bShearCorrected)
+        {
+            // back-correct shear
+            basegfx::B2DTuple aScale;
+            basegfx::B2DTuple aTranslate;
+            double fRotate(0.0), fShearX(0.0);
 
-    aRangeOriginalNoShearNoRotate.transform(aMatrixOriginalNoShearNoRotate);
-    aRangeNewNoShearNoRotate.transform(aMatrixOriginalNoShearNoRotate * aDiscreteChangeMatrix);
-
-    // extract the old Rectangle structures
-    tools::Rectangle aOldRect(
-        basegfx::fround(aRangeOriginalNoShearNoRotate.getMinX()),
-        basegfx::fround(aRangeOriginalNoShearNoRotate.getMinY()),
-        basegfx::fround(aRangeOriginalNoShearNoRotate.getMaxX()),
-        basegfx::fround(aRangeOriginalNoShearNoRotate.getMaxY()));
-    tools::Rectangle aNewRect(
-        basegfx::fround(aRangeNewNoShearNoRotate.getMinX()),
-        basegfx::fround(aRangeNewNoShearNoRotate.getMinY()),
-        basegfx::fround(aRangeNewNoShearNoRotate.getMaxX()),
-        basegfx::fround(aRangeNewNoShearNoRotate.getMaxY()));
-
-    // continue with the old original stuff
-    if (!aOldRect.GetWidth() || !aOldRect.GetHeight())
-        throw o3tl::divide_by_zero();
-
-    double fScaleX = ( aGraphicSize.Width() - rOldCrop.GetLeft() - rOldCrop.GetRight() ) / (double)aOldRect.GetWidth();
-    double fScaleY = ( aGraphicSize.Height() - rOldCrop.GetTop() - rOldCrop.GetBottom() ) / (double)aOldRect.GetHeight();
-
-    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();
-
-    if(pObj->IsMirrored())
-    {
-        // mirrored X or Y, for old stuff, exchange X
-        // check for aw080
-        sal_Int32 nTmp(nDiffLeft);
-        nDiffLeft = -nDiffRight;
-        nDiffRight = -nTmp;
-    }
-
-    sal_Int32 nLeftCrop = static_cast<sal_Int32>( rOldCrop.GetLeft() + nDiffLeft * fScaleX );
-    sal_Int32 nTopCrop = static_cast<sal_Int32>( rOldCrop.GetTop() + nDiffTop * fScaleY );
-    sal_Int32 nRightCrop = static_cast<sal_Int32>( rOldCrop.GetRight() - nDiffRight * fScaleX );
-    sal_Int32 nBottomCrop = static_cast<sal_Int32>( rOldCrop.GetBottom() - nDiffBottom * fScaleY );
-
-    SfxItemPool& rPool = getSdrDragView().GetModel()->GetItemPool();
-    SfxItemSet aSet( rPool, svl::Items<SDRATTR_GRAFCROP, SDRATTR_GRAFCROP>{} );
-    aSet.Put( SdrGrafCropItem( nLeftCrop, nTopCrop, nRightCrop, nBottomCrop ) );
-    getSdrDragView().SetAttributes( aSet, false );
-
-    if( bUndo )
+            aNewObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+            aNewObjectMatrix = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
+                aScale,
+                -fShearX,
+                fRotate,
+                aTranslate);
+        }
+
+        // apply change to object by applying the unit coordinate change followed
+        // by the original change
+        pObj->TRSetBaseGeometry(aNewObjectMatrix, aPolyPolygon);
+
+        // extract the old Rectangle structures
+        tools::Rectangle aOldRect(
+            basegfx::fround(aRangeOriginalNoShearNoRotate.getMinX()),
+            basegfx::fround(aRangeOriginalNoShearNoRotate.getMinY()),
+            basegfx::fround(aRangeOriginalNoShearNoRotate.getMaxX()),
+            basegfx::fround(aRangeOriginalNoShearNoRotate.getMaxY()));
+        tools::Rectangle aNewRect(
+            basegfx::fround(aRangeNewNoShearNoRotate.getMinX()),
+            basegfx::fround(aRangeNewNoShearNoRotate.getMinY()),
+            basegfx::fround(aRangeNewNoShearNoRotate.getMaxX()),
+            basegfx::fround(aRangeNewNoShearNoRotate.getMaxY()));
+
+        // continue with the old original stuff
+        if (!aOldRect.GetWidth() || !aOldRect.GetHeight())
+        {
+            throw o3tl::divide_by_zero();
+        }
+
+        if((pObj->GetGraphicType() == GraphicType::NONE) || (pObj->GetGraphicType() == GraphicType::Default))
+        {
+            return false;
+        }
+
+        const GraphicObject& rGraphicObject = pObj->GetGraphicObject();
+        const MapMode aMapMode100thmm(MapUnit::Map100thMM);
+        Size aGraphicSize(rGraphicObject.GetPrefSize());
+
+        if(MapUnit::MapPixel == rGraphicObject.GetPrefMapMode().GetMapUnit())
+        {
+            aGraphicSize = Application::GetDefaultDevice()->PixelToLogic(aGraphicSize, aMapMode100thmm);
+        }
+        else
+        {
+            aGraphicSize = OutputDevice::LogicToLogic(aGraphicSize, rGraphicObject.GetPrefMapMode(), aMapMode100thmm);
+        }
+
+        if(0 == aGraphicSize.Width() || 0 == aGraphicSize.Height())
+        {
+            return false;
+        }
+
+        const SdrGrafCropItem& rOldCrop = static_cast<const SdrGrafCropItem&>(pObj->GetMergedItem(SDRATTR_GRAFCROP));
+        double fScaleX = ( aGraphicSize.Width() - rOldCrop.GetLeft() - rOldCrop.GetRight() ) / (double)aOldRect.GetWidth();
+        double fScaleY = ( aGraphicSize.Height() - rOldCrop.GetTop() - rOldCrop.GetBottom() ) / (double)aOldRect.GetHeight();
+
+        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();
+
+        if(pObj->IsMirrored())
+        {
+            // mirrored X or Y, for old stuff, exchange X
+            // check for aw080
+            sal_Int32 nTmp(nDiffLeft);
+            nDiffLeft = -nDiffRight;
+            nDiffRight = -nTmp;
+        }
+
+        sal_Int32 nLeftCrop = static_cast<sal_Int32>( rOldCrop.GetLeft() + nDiffLeft * fScaleX );
+        sal_Int32 nTopCrop = static_cast<sal_Int32>( rOldCrop.GetTop() + nDiffTop * fScaleY );
+        sal_Int32 nRightCrop = static_cast<sal_Int32>( rOldCrop.GetRight() - nDiffRight * fScaleX );
+        sal_Int32 nBottomCrop = static_cast<sal_Int32>( rOldCrop.GetBottom() - nDiffBottom * fScaleY );
+
+        SfxItemPool& rPool = getSdrDragView().GetModel()->GetItemPool();
+        SfxItemSet aSet( rPool, svl::Items<SDRATTR_GRAFCROP, SDRATTR_GRAFCROP>{} );
+        aSet.Put( SdrGrafCropItem( nLeftCrop, nTopCrop, nRightCrop, nBottomCrop ) );
+        getSdrDragView().SetAttributes( aSet, false );
+    }
+
+    if(bUndo)
+    {
         getSdrDragView().EndUndo();
+    }
 
     return true;
 }
diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx
index 69a359539456..550f862b5754 100644
--- a/sw/source/core/doc/notxtfrm.cxx
+++ b/sw/source/core/doc/notxtfrm.cxx
@@ -757,53 +757,24 @@ bool paintUsingPrimitivesHelper(
     return false;
 }
 
-void paintGraphicUsingPrimitivesHelper(vcl::RenderContext & rOutputDevice,
-         GraphicObject const& rGrfObj, GraphicAttr const& rGraphicAttr,
-         SwRect const& rAlignedGrfArea)
+void paintGraphicUsingPrimitivesHelper(
+    vcl::RenderContext & rOutputDevice,
+    GraphicObject const& rGrfObj,
+    GraphicAttr const& rGraphicAttr,
+    SwRect const& rAlignedGrfArea)
 {
-    // unify using GraphicPrimitive2D
+    // RotGrfFlyFrame: unify using GraphicPrimitive2D
     // -> the primitive handles all crop and mirror stuff
     // -> the primitive renderer will create the needed pdf export data
     // -> if bitmap content, it will be cached system-dependent
     const basegfx::B2DRange aTargetRange(
         rAlignedGrfArea.Left(), rAlignedGrfArea.Top(),
         rAlignedGrfArea.Right(), rAlignedGrfArea.Bottom());
-    basegfx::B2DHomMatrix aTargetTransform;
-
-    // RotGrfFlyFrame: Take rotation into account. Rotation is in 10th degrees
-    if(0 != rGraphicAttr.GetRotation())
-    {
-        // Fit rotated graphic to center of available space, keeping page ratio:
-        // Adapt scaling ratio of unit object and rotate it
-        const double fRotate(static_cast< double >(-rGraphicAttr.GetRotation()) * (M_PI/1800.0));
-        aTargetTransform.scale(1.0, aTargetRange.getHeight() / aTargetRange.getWidth());
-        aTargetTransform.rotate(fRotate);
-
-        // get the range to see where we are in unit coordinates
-        basegfx::B2DRange aFullRange(0.0, 0.0, 1.0, 1.0);
-        aFullRange.transform(aTargetTransform);
-
-        // detect needed scales in X/Y and choose the smallest for staying inside the
-        // available space while keeping aspect ratio of the source
-        const double fScaleX(aTargetRange.getWidth() / aFullRange.getWidth());
-        const double fScaleY(aTargetRange.getHeight() / aFullRange.getHeight());
-        const double fScaleMin(std::min(fScaleX, fScaleY));
-
-        // TopLeft to zero, then scale, then move to center of available space
-        aTargetTransform.translate(-aFullRange.getMinX(), -aFullRange.getMinY());
-        aTargetTransform.scale(fScaleMin, fScaleMin);
-        aTargetTransform.translate(
-            aTargetRange.getCenterX() - (0.5 * fScaleMin * aFullRange.getWidth()),
-            aTargetRange.getCenterY() - (0.5 * fScaleMin * aFullRange.getHeight()));
-    }
-    else
-    {
-        // just scale/translate needed
-        aTargetTransform *= basegfx::tools::createScaleTranslateB2DHomMatrix(
-            aTargetRange.getRange(),
-            aTargetRange.getMinimum());
-    }
-
+    const double fRotate(static_cast< double >(-rGraphicAttr.GetRotation()) * (M_PI/1800.0));
+    const basegfx::B2DHomMatrix aTargetTransform(
+        basegfx::tools::createRotateAroundCenterKeepAspectRatioStayInsideRange(
+            aTargetRange,
+            fRotate));
     drawinglayer::primitive2d::Primitive2DContainer aContent(1);
     bool bDone(false);
 
diff --git a/sw/source/core/draw/dflyobj.cxx b/sw/source/core/draw/dflyobj.cxx
index c94f293ed876..770788d5d4dc 100644
--- a/sw/source/core/draw/dflyobj.cxx
+++ b/sw/source/core/draw/dflyobj.cxx
@@ -59,6 +59,8 @@
 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
 #include <sw_primitivetypes2d.hxx>
 #include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <notxtfrm.hxx>
 
 using namespace ::com::sun::star;
 
@@ -920,20 +922,96 @@ void SwVirtFlyDrawObj::Crop(const Point& rRef, const Fraction& xFact, const Frac
     GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false);
 }
 
-void SwVirtFlyDrawObj::addCropHandles(SdrHdlList& rTarget) const
+// RotGrfFlyFrame: Helper to access possible rotation of Graphic contained in FlyFrame
+sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame() const
 {
-    tools::Rectangle aRect(GetSnapRect());
+    sal_uInt16 nRetval(0);
+    const SwNoTextFrame* pNoTx = dynamic_cast< const SwNoTextFrame* >(GetFlyFrame()->Lower());
 
-    if(!aRect.IsEmpty())
+    if(pNoTx)
     {
-       rTarget.AddHdl(new SdrCropHdl(aRect.TopLeft()     , SdrHdlKind::UpperLeft, 0, 0));
-       rTarget.AddHdl(new SdrCropHdl(aRect.TopCenter()   , SdrHdlKind::Upper, 0, 0));
-       rTarget.AddHdl(new SdrCropHdl(aRect.TopRight()    , SdrHdlKind::UpperRight, 0, 0));
-       rTarget.AddHdl(new SdrCropHdl(aRect.LeftCenter()  , SdrHdlKind::Left , 0, 0));
-       rTarget.AddHdl(new SdrCropHdl(aRect.RightCenter() , SdrHdlKind::Right, 0, 0));
-       rTarget.AddHdl(new SdrCropHdl(aRect.BottomLeft()  , SdrHdlKind::LowerLeft, 0, 0));
-       rTarget.AddHdl(new SdrCropHdl(aRect.BottomCenter(), SdrHdlKind::Lower, 0, 0));
-       rTarget.AddHdl(new SdrCropHdl(aRect.BottomRight() , SdrHdlKind::LowerRight, 0, 0));
+        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();
+
+            nRetval = rRotation.GetValue();
+        }
+    }
+
+    return nRetval;
+}
+
+SdrObject* SwVirtFlyDrawObj::getFullDragClone() const
+{
+    // call parent
+    SdrObject* pRetval = SdrVirtObj::getFullDragClone();
+
+    if(pRetval)
+    {
+        // RotGrfFlyFrame: Add transformation to placeholder object
+        const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame());
+
+        if(0 != nRotation)
+        {
+            const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0));
+            const tools::Rectangle aOutRect(GetFlyFrame()->Frame().SVRect());
+            const basegfx::B2DRange aTargetRange(
+                aOutRect.Left(), aOutRect.Top(),
+                aOutRect.Right(), aOutRect.Bottom());
+            const basegfx::B2DHomMatrix aTargetTransform(
+                basegfx::tools::createRotateAroundCenterKeepAspectRatioStayInsideRange(
+                    aTargetRange,
+                    fRotate));
+
+            pRetval->TRSetBaseGeometry(aTargetTransform, basegfx::B2DPolyPolygon());
+        }
+    }
+
+    return pRetval;
+}
+
+void SwVirtFlyDrawObj::addCropHandles(SdrHdlList& rTarget) const
+{
+    // RotGrfFlyFrame: Adapt to possible rotated Graphic contained in FlyFrame
+    if(GetFlyFrame()->Frame().HasArea())
+    {
+        const tools::Rectangle aOutRect(GetFlyFrame()->Frame().SVRect());
+        const basegfx::B2DRange aTargetRange(
+            aOutRect.Left(), aOutRect.Top(),
+            aOutRect.Right(), aOutRect.Bottom());
+
+        if(!aTargetRange.isEmpty())
+        {
+            const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame());
+            const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0));
+            const basegfx::B2DHomMatrix aTargetTransform(
+                basegfx::tools::createRotateAroundCenterKeepAspectRatioStayInsideRange(
+                    aTargetRange,
+                    fRotate));
+            basegfx::B2DPoint aPos;
+            const double fShearX(0.0);
+
+            aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.0);
+            rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperLeft, fShearX, fRotate));
+            aPos = aTargetTransform * basegfx::B2DPoint(0.5, 0.0);
+            rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Upper, fShearX, fRotate));
+            aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.0);
+            rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperRight, fShearX, fRotate));
+            aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.5);
+            rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Left , fShearX, fRotate));
+            aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.5);
+            rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Right, fShearX, fRotate));
+            aPos = aTargetTransform * basegfx::B2DPoint(0.0, 1.0);
+            rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerLeft, fShearX, fRotate));
+            aPos = aTargetTransform * basegfx::B2DPoint(0.5, 1.0);
+            rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Lower, fShearX, fRotate));
+            aPos = aTargetTransform * basegfx::B2DPoint(1.0, 1.0);
+            rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerRight, fShearX, fRotate));
+        }
     }
 }
 
diff --git a/sw/source/core/inc/dflyobj.hxx b/sw/source/core/inc/dflyobj.hxx
index 9368eb893b07..528edc8aff39 100644
--- a/sw/source/core/inc/dflyobj.hxx
+++ b/sw/source/core/inc/dflyobj.hxx
@@ -58,6 +58,10 @@ class SwVirtFlyDrawObj : public SdrVirtObj
 private:
     SwFlyFrame *m_pFlyFrame;
 
+    // RotGrfFlyFrame: Helper to acces sthe rotation angle (in 10th degrees, left-handed)
+    // of a GraphicFrame
+    sal_uInt16 getPossibleRotationFromFraphicFrame() const;
+
 protected:
     // AW: Need own sdr::contact::ViewContact since AnchorPos from parent is
     // not used but something own (top left of new SnapRect minus top left
@@ -102,6 +106,9 @@ public:
     virtual       void       Crop(const Point& rRef, const Fraction& xFact, const Fraction& yFact) override;
     virtual       void       addCropHandles(SdrHdlList& rTarget) const override;
 
+    // FullDrag support
+    virtual SdrObject* getFullDragClone() const override;
+
     const SwFrameFormat *GetFormat() const;
           SwFrameFormat *GetFormat();
 
diff --git a/sw/source/core/inc/frmtool.hxx b/sw/source/core/inc/frmtool.hxx
index 31f96590cd8b..e1a894385f37 100644
--- a/sw/source/core/inc/frmtool.hxx
+++ b/sw/source/core/inc/frmtool.hxx
@@ -72,10 +72,11 @@ bool DrawFillAttributes(
     const basegfx::tools::B2DClipState& rClipState,
     OutputDevice& rOut);
 
+// RotGrfFlyFrame: Adapted to rotation
 void paintGraphicUsingPrimitivesHelper(
-        OutputDevice & rOutputDevice,
-        GraphicObject const& rGraphicObj, GraphicAttr const& rGraphicAttr,
-        SwRect const& rAlignedGrfArea);
+    OutputDevice & rOutputDevice,
+    GraphicObject const& rGraphicObj, GraphicAttr const& rGraphicAttr,
+    SwRect const& rAlignedGrfArea);
 
 // method to align rectangle.
 // Created declaration here to avoid <extern> declarations


More information about the Libreoffice-commits mailing list