[Libreoffice-commits] core.git: Branch 'feature/RotGrfFlyFrame' - 442 commits - accessibility/inc accessibility/source apple_remote/source avmedia/inc avmedia/Library_avmedia.mk avmedia/Library_avmediavlc.mk avmedia/source basctl/source basctl/uiconfig basegfx/source basic/inc basic/qa basic/source binaryurp/source bin/lo-all-static-libs bridges/inc bridges/Library_cpp_uno.mk chart2/source chart2/uiconfig codemaker/source compilerplugins/clang config_host/config_global.h.in config_host/config_gpgme.h.in config_host.mk.in configmgr/source configure.ac connectivity/source cppcanvas/Library_cppcanvas.mk cppcanvas/source cui/source cui/uiconfig dbaccess/source dbaccess/uiconfig desktop/source dictionaries distro-configs/LibreOfficeiOS.conf download.lst drawinglayer/inc drawinglayer/source editeng/CppunitTest_editeng_core.mk editeng/inc editeng/qa editeng/source extensions/source extensions/uiconfig external/expat external/gpgme external/hyphen external/icu external/lcms2 external/libepubgen external/ libexttextcat external/libjpeg-turbo external/liblangtag external/librevenge external/libxml2 external/pdfium extras/source filter/qa filter/source filter/uiconfig forms/source formula/uiconfig framework/qa framework/source helpcontent2 hwpfilter/source i18npool/source icon-themes/breeze icon-themes/breeze_dark icon-themes/breeze_svg icon-themes/sifr icon-themes/sifr_dark icon-themes/sifr_svg icon-themes/tango idlc/source include/basegfx include/codemaker include/comphelper include/cppcanvas include/editeng include/filter include/LibreOfficeKit include/o3tl include/oox include/registry include/rtl include/sal include/sfx2 include/svl include/svtools include/svx include/test include/toolkit include/tools include/unotools include/vcl include/xmloff ios/CustomTarget_iOS.mk ios/LibreOfficeKit ios/LibreOfficeLight ios/loApp.xcconfig.in jvmfwk/plugins jvmfwk/source lingucomponent/Library_hyphen.mk lingucomponent/Library_lnth.mk lingucomponent/Library_MacOSXSpell.mk lingucomponent/Library_ spell.mk lingucomponent/source lotuswordpro/inc lotuswordpro/Library_lwpft.mk lotuswordpro/source mysqlc/source odk/examples offapi/com officecfg/registry oox/inc oox/source package/source qadevOOo/Jar_OOoRunner.mk qadevOOo/objdsc qadevOOo/tests README.md registry/source reportdesign/source reportdesign/uiconfig sal/osl sal/rtl sal/textenc scaddins/source sc/CppunitTest_sc_arealinksobj.mk sc/CppunitTest_sc_databaserangeobj.mk sc/CppunitTest_sc_datapilottableobj.mk sc/CppunitTest_sc_namedrangeobj.mk sc/CppunitTest_sc_namedrangesobj.mk sc/CppunitTest_sc_subtotalfieldobj.mk sc/CppunitTest_sc_tablesheetobj.mk sc/inc sc/Module_sc.mk sc/qa sc/README sc/source sc/uiconfig sc/UIConfig_scalc.mk sdext/source sd/inc sd/Library_sdfilt.mk sd/qa sd/source sd/uiconfig sfx2/sdi sfx2/source sfx2/uiconfig slideshow/source solenv/bin solenv/CompilerTest_compilerplugins_clang.mk solenv/gbuild sot/source starmath/source store/source svl/qa svl/source svtools/source svx/sdi svx/source svx/uiconfig sw/Cpp unitTest_sw_uiwriter.mk sw/inc sw/qa sw/sdi sw/source sw/uiconfig sw/UIConfig_swriter.mk test/Library_subsequenttest.mk test/source toolkit/source tools/source translations udkapi/com uitest/writer_tests unoidl/source unotools/source unoxml/inc unoxml/source uui/source vcl/Executable_602fuzzer.mk vcl/Executable_bmpfuzzer.mk vcl/Executable_dxffuzzer.mk vcl/Executable_epsfuzzer.mk vcl/Executable_giffuzzer.mk vcl/Executable_hwpfuzzer.mk vcl/Executable_jpgfuzzer.mk vcl/Executable_lwpfuzzer.mk vcl/Executable_metfuzzer.mk vcl/Executable_olefuzzer.mk vcl/Executable_pcdfuzzer.mk vcl/Executable_pctfuzzer.mk vcl/Executable_pcxfuzzer.mk vcl/Executable_pngfuzzer.mk vcl/Executable_ppmfuzzer.mk vcl/Executable_psdfuzzer.mk vcl/Executable_rasfuzzer.mk vcl/Executable_svmfuzzer.mk vcl/Executable_tgafuzzer.mk vcl/Executable_tiffuzzer.mk vcl/Executable_wmffuzzer.mk vcl/Executable_ww2fuzzer.mk vcl/Executable_xbmfuzzer.mk vcl/Executable_xpmfuzzer.mk vcl/inc vcl/ios vcl/osx vcl/qa vcl/README.scheduler vcl /source vcl/uiconfig vcl/unx vcl/win vcl/workben writerfilter/source writerperfect/qa writerperfect/source xmloff/inc xmloff/source xmlscript/source xmlsecurity/inc xmlsecurity/source xmlsecurity/uiconfig

Armin Le Grand Armin.Le.Grand at cib.de
Fri Oct 20 09:48:49 UTC 2017


Rebased ref, commits from common ancestor:
commit fd1f9044f0616981776ba8e7c315a1cbc045a865
Author: Armin Le Grand <Armin.Le.Grand at cib.de>
Date:   Thu Oct 5 22:00:35 2017 +0200

    RotGrfFlyFrame: Clang error fix
    
    Change-Id: Ifae68d4d5a17446f01c97ce2e94cd0419217259e

diff --git a/sw/source/core/draw/dview.cxx b/sw/source/core/draw/dview.cxx
index 7db8e4946abd..0935ddbd637c 100644
--- a/sw/source/core/draw/dview.cxx
+++ b/sw/source/core/draw/dview.cxx
@@ -829,11 +829,7 @@ void SwDrawView::CheckPossibilities()
                 pFrame = pFly->GetAnchorFrame();
                 if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
                 {
-                    // SwNoTextNode& rNoTNd = const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>((static_cast<const SwContentFrame*>(pFly->Lower()))->GetNode()));
-                    // SwGrfNode* pGrfNd = rNoTNd.GetGrfNode();
-                    // SwOLENode* pOLENd = rNoTNd.GetOLENode();
-
-                    const SwContentFrame* pCntFr = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pFly->Lower()));
+                    const SwContentFrame* pCntFr(static_cast<const SwContentFrame*>(pFly->Lower()));
                     const SwOLENode* pOLENd = pCntFr->GetNode()->GetOLENode();
                     const SwGrfNode* pGrfNd = pCntFr->GetNode()->GetGrfNode();
 
commit 35f828b68ec267479eca3aabe034f8bf81221502
Author: Armin Le Grand <Armin.Le.Grand at cib.de>
Date:   Wed Oct 4 17:44:24 2017 +0200

    RotGrfFlyFrame: Added interactive rotation mode
    
    The FlyFrames containing a graphic now support an
    interactive rotation mode. Added a rotation icon to the
    Toolbar close to right/left 90degree rotation. When
    activated, works as similar to draw object mode as
    possible. Shear and move of the rotation center is
    deactivated since not supported. It uses as much of the
    existing interaction stuff as possible.
    
    Change-Id: Ia1a4e5c064d8576b114c3fcf3a96ccb42c9372bb

diff --git a/basegfx/source/matrix/b2dhommatrixtools.cxx b/basegfx/source/matrix/b2dhommatrixtools.cxx
index 26936aff2e5c..e26fb922f5b6 100644
--- a/basegfx/source/matrix/b2dhommatrixtools.cxx
+++ b/basegfx/source/matrix/b2dhommatrixtools.cxx
@@ -364,7 +364,8 @@ namespace basegfx
         {
             basegfx::B2DHomMatrix aRetval;
 
-            // RotGrfFlyFrame: Take rotation into account. Rotation is in 10th degrees
+            // RotGrfFlyFrame: Create a transformation that maps the range inside of itself
+            // so that it fits, takes as much space as possible and keeps the aspect ratio
             if(0.0 != fRotate)
             {
                 // Fit rotated graphic to center of available space, keeping page ratio:
diff --git a/include/svx/svdobj.hxx b/include/svx/svdobj.hxx
index 3d7ded0a7fe9..cd6953312565 100644
--- a/include/svx/svdobj.hxx
+++ b/include/svx/svdobj.hxx
@@ -410,6 +410,10 @@ public:
     void SingleObjectPainter(OutputDevice& rOut) const;
     bool LineGeometryUsageIsNecessary() const;
 
+    // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation, that
+    // means no change of the rotation point (only centered) and no shear allowed
+    virtual bool HasLimitedRotation() const;
+
     // Returns a copy of the object. Every inherited class must reimplement this (in class Foo
     // it should be sufficient to do "virtual Foo* Clone() const { return CloneHelper< Foo >(); }".
     // Note that this function uses operator= internally.
diff --git a/include/svx/svdovirt.hxx b/include/svx/svdovirt.hxx
index a6223975572d..c6d626e1305f 100644
--- a/include/svx/svdovirt.hxx
+++ b/include/svx/svdovirt.hxx
@@ -68,6 +68,9 @@ public:
     virtual OUString TakeObjNameSingul() const override;
     virtual OUString TakeObjNamePlural() const override;
 
+    // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation
+    virtual bool HasLimitedRotation() const override;
+
     virtual basegfx::B2DPolyPolygon TakeXorPoly() const override;
     virtual sal_uInt32 GetHdlCount() const override;
     virtual SdrHdl* GetHdl(sal_uInt32 nHdlNum) const override;
diff --git a/include/svx/svxids.hrc b/include/svx/svxids.hrc
index 79ebc50726c1..8d0e22a22179 100644
--- a/include/svx/svxids.hrc
+++ b/include/svx/svxids.hrc
@@ -919,7 +919,7 @@
 #define SID_ROTATE_GRAPHIC_LEFT                         ( SID_SVX_START + 1121 )
 #define SID_ROTATE_GRAPHIC_RIGHT                        ( SID_SVX_START + 1122 )
 #define SID_ROTATE_GRAPHIC_180                          ( SID_SVX_START + 1123 )
-#define SID_ROTATE_GRAPHIC_RESET                        ( SID_SVX_START + 1092 ) /* RotGrfFlyFrame: new slot */
+#define SID_ROTATE_GRAPHIC_RESET                        ( SID_SVX_START + 1092 ) /* RotGrfFlyFrame: */
 
 // new slots for panels
 #define SID_ATTR_FILL_TRANSPARENCE                      ( SID_SVX_START + 1124 )
diff --git a/svx/source/svdraw/svddrgmt.cxx b/svx/source/svdraw/svddrgmt.cxx
index 3348579bd508..a00528f61af8 100644
--- a/svx/source/svdraw/svddrgmt.cxx
+++ b/svx/source/svdraw/svddrgmt.cxx
@@ -2097,18 +2097,28 @@ bool SdrDragRotate::BeginSdrDrag()
 {
     SdrHdl* pH=GetHdlList().GetHdl(SdrHdlKind::Ref1);
 
-    if (pH!=nullptr)
+    if (nullptr != pH)
     {
         Show();
         DragStat().Ref1()=pH->GetPos();
         nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1());
         return true;
     }
-    else
+
+    // RotGrfFlyFrame: Support rotation around center *without* Ref1 (normally
+    // the rotation point)
+    const tools::Rectangle aLocalMarkRect(getSdrDragView().GetMarkedObjRect());
+
+    if(!aLocalMarkRect.IsEmpty())
     {
-        OSL_FAIL("SdrDragRotate::BeginSdrDrag(): No reference point handle found.");
-        return false;
+        Show();
+        DragStat().Ref1() = aLocalMarkRect.Center();
+        nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1());
+        return true;
     }
+
+    OSL_FAIL("SdrDragRotate::BeginSdrDrag(): No reference point handle found.");
+    return false;
 }
 
 basegfx::B2DHomMatrix SdrDragRotate::getCurrentTransformation()
diff --git a/svx/source/svdraw/svdmrkv.cxx b/svx/source/svdraw/svdmrkv.cxx
index 73a79d4bb983..61286258cc0f 100644
--- a/svx/source/svdraw/svdmrkv.cxx
+++ b/svx/source/svdraw/svdmrkv.cxx
@@ -658,14 +658,22 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
         const size_t nMarkCount=GetMarkedObjectCount();
         bool bStdDrag=meDragMode==SdrDragMode::Move;
         bool bSingleTextObjMark=false;
+        bool bLimitedRotation(false);
 
         if (nMarkCount==1)
         {
             mpMarkedObj=GetMarkedObjectByIndex(0);
-            bSingleTextObjMark =
-                mpMarkedObj &&
-                dynamic_cast<const SdrTextObj*>( mpMarkedObj) !=  nullptr &&
-                static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame();
+
+            if(nullptr != mpMarkedObj)
+            {
+                bSingleTextObjMark =
+                    mpMarkedObj &&
+                    dynamic_cast<const SdrTextObj*>( mpMarkedObj) !=  nullptr &&
+                    static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame();
+
+                // RotGrfFlyFrame: we may have limited rotation
+                bLimitedRotation = SdrDragMode::Rotate == meDragMode && mpMarkedObj->HasLimitedRotation();
+            }
         }
 
         bool bFrmHdl=ImpIsFrameHandles();
@@ -831,27 +839,59 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
                 }
                 else
                 {
-                    bool bWdt0=aRect.Left()==aRect.Right();
-                    bool bHgt0=aRect.Top()==aRect.Bottom();
+                    const bool bWdt0(aRect.Left() == aRect.Right());
+                    const bool bHgt0(aRect.Top() == aRect.Bottom());
+
                     if (bWdt0 && bHgt0)
                     {
-                        maHdlList.AddHdl(new SdrHdl(aRect.TopLeft(),SdrHdlKind::UpperLeft));
+                        maHdlList.AddHdl(new SdrHdl(aRect.TopLeft(), SdrHdlKind::UpperLeft));
                     }
                     else if (!bStdDrag && (bWdt0 || bHgt0))
                     {
-                        maHdlList.AddHdl(new SdrHdl(aRect.TopLeft()    ,SdrHdlKind::UpperLeft));
-                        maHdlList.AddHdl(new SdrHdl(aRect.BottomRight(),SdrHdlKind::LowerRight));
+                        maHdlList.AddHdl(new SdrHdl(aRect.TopLeft(), SdrHdlKind::UpperLeft));
+                        maHdlList.AddHdl(new SdrHdl(aRect.BottomRight(), SdrHdlKind::LowerRight));
                     }
                     else
                     {
-                        if (!bWdt0 && !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.TopLeft()     ,SdrHdlKind::UpperLeft));
-                        if (          !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.TopCenter()   ,SdrHdlKind::Upper));
-                        if (!bWdt0 && !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.TopRight()    ,SdrHdlKind::UpperRight));
-                        if (!bWdt0          ) maHdlList.AddHdl(new SdrHdl(aRect.LeftCenter()  ,SdrHdlKind::Left ));
-                        if (!bWdt0          ) maHdlList.AddHdl(new SdrHdl(aRect.RightCenter() ,SdrHdlKind::Right));
-                        if (!bWdt0 && !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.BottomLeft()  ,SdrHdlKind::LowerLeft));
-                        if (          !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.BottomCenter(),SdrHdlKind::Lower));
-                        if (!bWdt0 && !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.BottomRight() ,SdrHdlKind::LowerRight));
+                        if (!bWdt0 && !bHgt0)
+                        {
+                            maHdlList.AddHdl(new SdrHdl(aRect.TopLeft(), SdrHdlKind::UpperLeft));
+                        }
+
+                        if (!bLimitedRotation && !bHgt0)
+                        {
+                            maHdlList.AddHdl(new SdrHdl(aRect.TopCenter(), SdrHdlKind::Upper));
+                        }
+
+                        if (!bWdt0 && !bHgt0)
+                        {
+                            maHdlList.AddHdl(new SdrHdl(aRect.TopRight(), SdrHdlKind::UpperRight));
+                        }
+
+                        if (!bLimitedRotation && !bWdt0)
+                        {
+                            maHdlList.AddHdl(new SdrHdl(aRect.LeftCenter(), SdrHdlKind::Left ));
+                        }
+
+                        if (!bLimitedRotation && !bWdt0)
+                        {
+                            maHdlList.AddHdl(new SdrHdl(aRect.RightCenter(), SdrHdlKind::Right));
+                        }
+
+                        if (!bWdt0 && !bHgt0)
+                        {
+                            maHdlList.AddHdl(new SdrHdl(aRect.BottomLeft(), SdrHdlKind::LowerLeft));
+                        }
+
+                        if (!bLimitedRotation && !bHgt0)
+                        {
+                            maHdlList.AddHdl(new SdrHdl(aRect.BottomCenter(), SdrHdlKind::Lower));
+                        }
+
+                        if (!bWdt0 && !bHgt0)
+                        {
+                            maHdlList.AddHdl(new SdrHdl(aRect.BottomRight(), SdrHdlKind::LowerRight));
+                        }
                     }
                 }
             }
@@ -945,7 +985,10 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
         }
 
         // rotation point/axis of reflection
-        AddDragModeHdl(meDragMode);
+        if(!bLimitedRotation)
+        {
+            AddDragModeHdl(meDragMode);
+        }
 
         // sort handles
         maHdlList.Sort();
diff --git a/svx/source/svdraw/svdobj.cxx b/svx/source/svdraw/svdobj.cxx
index f5f3f269f866..b2dd3a2fc6c8 100644
--- a/svx/source/svdraw/svdobj.cxx
+++ b/svx/source/svdraw/svdobj.cxx
@@ -931,6 +931,12 @@ bool SdrObject::LineGeometryUsageIsNecessary() const
     return (eXLS != drawing::LineStyle_NONE);
 }
 
+bool SdrObject::HasLimitedRotation() const
+{
+    // RotGrfFlyFrame: Default is false, support full rotation
+    return false;
+}
+
 SdrObject* SdrObject::Clone() const
 {
     return CloneHelper< SdrObject >();
diff --git a/svx/source/svdraw/svdovirt.cxx b/svx/source/svdraw/svdovirt.cxx
index 7c9f54e35bd8..d7145be8f93a 100644
--- a/svx/source/svdraw/svdovirt.cxx
+++ b/svx/source/svdraw/svdovirt.cxx
@@ -166,6 +166,12 @@ OUString SdrVirtObj::TakeObjNamePlural() const
     return sName.makeStringAndClear();
 }
 
+bool SdrVirtObj::HasLimitedRotation() const
+{
+    // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation
+    return rRefObj.HasLimitedRotation();
+}
+
 basegfx::B2DPolyPolygon SdrVirtObj::TakeXorPoly() const
 {
     basegfx::B2DPolyPolygon aPolyPolygon(rRefObj.TakeXorPoly());
diff --git a/sw/inc/fesh.hxx b/sw/inc/fesh.hxx
index 0775d14cd852..1141a4ea9812 100644
--- a/sw/inc/fesh.hxx
+++ b/sw/inc/fesh.hxx
@@ -505,6 +505,9 @@ public:
     // Start cropping the selected image
     void StartCropImage();
 
+    // RotGrfFlyFrame: check if RotationMode is possibe
+    bool IsRotationOfSwGrfNodePossible() const;
+
     size_t IsObjSelected() const;   ///< @return object count, but doesn't count the objects in groups.
     bool IsObjSelected( const SdrObject& rObj ) const;
     bool IsObjSameLevelWithMarked(const SdrObject* pObj) const;
diff --git a/sw/sdi/grfsh.sdi b/sw/sdi/grfsh.sdi
index 956a0c82b443..8451eae60eba 100644
--- a/sw/sdi/grfsh.sdi
+++ b/sw/sdi/grfsh.sdi
@@ -24,5 +24,14 @@ shell SwGrfShell : SwBaseShell
 
 {
     import TextGraphic;
+
+    // RotGrfFlyFrame: need SID_OBJECT_ROTATE for FlyFrames with graphic
+    SID_OBJECT_ROTATE
+    [
+        Export = FALSE;
+        ExecMethod = Execute ;
+        StateMethod = GetAttrState ;
+        DisableFlags="SfxDisableFlags::SwOnProtectedCursor";
+    ]
 }
 
diff --git a/sw/source/core/draw/dflyobj.cxx b/sw/source/core/draw/dflyobj.cxx
index 6a21d0eb9d71..29e5e4a94715 100644
--- a/sw/source/core/draw/dflyobj.cxx
+++ b/sw/source/core/draw/dflyobj.cxx
@@ -49,6 +49,7 @@
 #include "rootfrm.hxx"
 #include "wrtsh.hxx"
 #include <ndgrf.hxx>
+#include <frmmgr.hxx>
 
 #include <svx/sdr/properties/defaultproperties.hxx>
 #include <basegfx/range/b2drange.hxx>
@@ -354,6 +355,64 @@ basegfx::B2DRange SwVirtFlyDrawObj::getInnerBound() const
     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 SwContentFrame* pCntFr(static_cast<const SwContentFrame*>(pFlyFrame->Lower()));
+
+        if(nullptr != pCntFr)
+        {
+            const SwGrfNode* pGrfNd(pCntFr->GetNode()->GetGrfNode());
+
+            return nullptr != pGrfNd;
+        }
+    }
+
+    return false;
+}
+
+bool SwVirtFlyDrawObj::HasLimitedRotation() const
+{
+    // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation.
+    // This is the case for SwGrfNode instances
+    return ContainsSwGrfNode();
+}
+
+void SwVirtFlyDrawObj::Rotate(const Point& rRef, long nAngle, double sn, double cs)
+{
+    if(ContainsSwGrfNode())
+    {
+        // RotGrfFlyFrame: Here is where the positively completed rotate interaction is executed.
+        // Rotation is in 1/100th degree and may be signed (!)
+        nAngle /= 10;
+
+        while(nAngle < 0)
+        {
+            nAngle += 3600;
+        }
+
+        if(0 != nAngle)
+        {
+            // RotGrfFlyFrame: Add transformation to placeholder object
+            Size aSize;
+            const sal_uInt16 nOldRot(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize));
+            SwWrtShell *pSh = dynamic_cast<SwWrtShell*>( GetFlyFrame()->getRootFrame()->GetCurrShell() );
+            SwFlyFrameAttrMgr aMgr(false, pSh, Frmmgr_Type::NONE);
+
+            aMgr.SetRotation(nOldRot, (nOldRot + static_cast<sal_uInt16>(nAngle)) % 3600, aSize);
+        }
+    }
+    else
+    {
+        // call parent
+        SdrVirtObj::Rotate(rRef, nAngle, sn, cs);
+    }
+}
+
 sdr::contact::ViewContact* SwVirtFlyDrawObj::CreateObjectSpecificViewContact()
 {
     // need an own ViewContact (VC) to allow creation of a specialized primitive
@@ -477,7 +536,9 @@ void SwVirtFlyDrawObj::TakeObjInfo( SdrObjTransformInfoRec& rInfo ) const
     rInfo.bMoveAllowed =
     rInfo.bResizeFreeAllowed = rInfo.bResizePropAllowed = true;
 
-    rInfo.bRotateFreeAllowed = rInfo.bRotate90Allowed =
+    // RotGrfFlyFrame: Some rotation may be allowed
+    rInfo.bRotateFreeAllowed = rInfo.bRotate90Allowed = HasLimitedRotation();
+
     rInfo.bMirrorFreeAllowed = rInfo.bMirror45Allowed =
     rInfo.bMirror90Allowed   = rInfo.bShearAllowed    =
     rInfo.bCanConvToPath     = rInfo.bCanConvToPoly   =
@@ -924,7 +985,7 @@ void SwVirtFlyDrawObj::Crop(const Point& rRef, const Fraction& xFact, const Frac
 }
 
 // RotGrfFlyFrame: Helper to access possible rotation of Graphic contained in FlyFrame
-sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame() const
+sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(Size& rSize) const
 {
     sal_uInt16 nRetval(0);
     const SwNoTextFrame* pNoTx = dynamic_cast< const SwNoTextFrame* >(GetFlyFrame()->Lower());
@@ -939,6 +1000,7 @@ sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame() const
             const SwAttrSet& rSet = pGrfNd->GetSwAttrSet();
             const SwRotationGrf& rRotation = rSet.GetRotationGrf();
 
+            rSize = rRotation.GetUnrotatedSize();
             nRetval = rRotation.GetValue();
         }
     }
@@ -951,25 +1013,19 @@ SdrObject* SwVirtFlyDrawObj::getFullDragClone() const
     // call parent
     SdrObject* pRetval = SdrVirtObj::getFullDragClone();
 
-    if(pRetval)
+    if(pRetval && ContainsSwGrfNode())
     {
         // 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 aTmpOutRect(GetFlyFrame()->Frame().SVRect());
-            const basegfx::B2DRange aTargetRange(
-                aTmpOutRect.Left(), aTmpOutRect.Top(),
-                aTmpOutRect.Right(), aTmpOutRect.Bottom());
-            const basegfx::B2DHomMatrix aTargetTransform(
-                basegfx::utils::createRotateAroundCenterKeepAspectRatioStayInsideRange(
-                    aTargetRange,
-                    fRotate));
+        Size aSize;
+        const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize));
+        const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0));
+        const basegfx::B2DRange aTargetRange(getInnerBound());
+        const basegfx::B2DHomMatrix aTargetTransform(
+            basegfx::utils::createRotateAroundCenterKeepAspectRatioStayInsideRange(
+                aTargetRange,
+                fRotate));
 
-            pRetval->TRSetBaseGeometry(aTargetTransform, basegfx::B2DPolyPolygon());
-        }
+        pRetval->TRSetBaseGeometry(aTargetTransform, basegfx::B2DPolyPolygon());
     }
 
     return pRetval;
@@ -987,7 +1043,8 @@ void SwVirtFlyDrawObj::addCropHandles(SdrHdlList& rTarget) const
 
         if(!aTargetRange.isEmpty())
         {
-            const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame());
+            Size aSize;
+            const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize));
             const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0));
             const basegfx::B2DHomMatrix aTargetTransform(
                 basegfx::utils::createRotateAroundCenterKeepAspectRatioStayInsideRange(
diff --git a/sw/source/core/draw/dview.cxx b/sw/source/core/draw/dview.cxx
index d7c995ef552e..7db8e4946abd 100644
--- a/sw/source/core/draw/dview.cxx
+++ b/sw/source/core/draw/dview.cxx
@@ -44,6 +44,7 @@
 #include "doc.hxx"
 #include "mdiexp.hxx"
 #include <ndole.hxx>
+#include <ndgrf.hxx>
 #include <fmtanchr.hxx>
 #include "shellres.hxx"
 #include <IDocumentUndoRedo.hxx>
@@ -814,6 +815,8 @@ void SwDrawView::CheckPossibilities()
     const SdrMarkList &rMrkList = GetMarkedObjectList();
     bool bProtect = false;
     bool bSzProtect = false;
+    bool bRotate(false);
+
     for ( size_t i = 0; !bProtect && i < rMrkList.GetMarkCount(); ++i )
     {
         const SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
@@ -826,10 +829,18 @@ void SwDrawView::CheckPossibilities()
                 pFrame = pFly->GetAnchorFrame();
                 if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
                 {
-                    SwOLENode *pNd = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pFly->Lower()))->GetNode()->GetOLENode();
-                    if ( pNd )
+                    // SwNoTextNode& rNoTNd = const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>((static_cast<const SwContentFrame*>(pFly->Lower()))->GetNode()));
+                    // SwGrfNode* pGrfNd = rNoTNd.GetGrfNode();
+                    // SwOLENode* pOLENd = rNoTNd.GetOLENode();
+
+                    const SwContentFrame* pCntFr = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pFly->Lower()));
+                    const SwOLENode* pOLENd = pCntFr->GetNode()->GetOLENode();
+                    const SwGrfNode* pGrfNd = pCntFr->GetNode()->GetGrfNode();
+
+                    if ( pOLENd )
                     {
-                        uno::Reference < embed::XEmbeddedObject > xObj = pNd->GetOLEObj().GetOleRef();
+                        const uno::Reference < embed::XEmbeddedObject > xObj = const_cast< SwOLEObj& >(pOLENd->GetOLEObj()).GetOleRef();
+
                         if ( xObj.is() )
                         {
                             // --> improvement for the future, when more
@@ -847,6 +858,13 @@ void SwDrawView::CheckPossibilities()
                                 bMoveProtect = true;
                         }
                     }
+                    else if(pGrfNd)
+                    {
+                        // RotGrfFlyFrame: GraphicNode allows rotation(s). The loop ew are in stops
+                        // as soon as bMoveProtect is set, but since rotation is valid only with
+                        // a single object selected this makes no difference
+                        bRotate = true;
+                    }
                 }
             }
         }
@@ -874,6 +892,10 @@ void SwDrawView::CheckPossibilities()
     }
     bMoveProtect    |= bProtect;
     bResizeProtect  |= bProtect || bSzProtect;
+
+    // RotGrfFlyFrame: allow rotation when SwGrfNode is selected and not size protected
+    bRotateFreeAllowed |= bRotate && !bProtect;
+    bRotate90Allowed |= bRotateFreeAllowed;
 }
 
 /// replace marked <SwDrawVirtObj>-objects by its reference object for delete marked objects.
diff --git a/sw/source/core/frmedt/feshview.cxx b/sw/source/core/frmedt/feshview.cxx
index 5ed4bdae28a8..27893c02fae6 100644
--- a/sw/source/core/frmedt/feshview.cxx
+++ b/sw/source/core/frmedt/feshview.cxx
@@ -1161,6 +1161,29 @@ bool SwFEShell::IsObjSelected( const SdrObject& rObj ) const
         return Imp()->GetDrawView()->IsObjMarked( &rObj );
 }
 
+bool SwFEShell::IsRotationOfSwGrfNodePossible() const
+{
+    // RotGrfFlyFrame: check if RotationMode is possibe
+    const SdrView *pSdrView = Imp()->GetDrawView();
+
+    if(pSdrView)
+    {
+        const SdrMarkList& rList(pSdrView->GetMarkedObjectList());
+
+        if(1 == rList.GetMarkCount())
+        {
+            const SwVirtFlyDrawObj* pVirtFlyDraw(dynamic_cast< const SwVirtFlyDrawObj* >(rList.GetMark(0)->GetMarkedSdrObj()));
+
+            if(nullptr != pVirtFlyDraw)
+            {
+                return pVirtFlyDraw->ContainsSwGrfNode();
+            }
+        }
+    }
+
+    return false;
+}
+
 bool SwFEShell::IsObjSameLevelWithMarked(const SdrObject* pObj) const
 {
     if (pObj)
diff --git a/sw/source/core/inc/dflyobj.hxx b/sw/source/core/inc/dflyobj.hxx
index 528edc8aff39..8766b7980ebc 100644
--- a/sw/source/core/inc/dflyobj.hxx
+++ b/sw/source/core/inc/dflyobj.hxx
@@ -60,7 +60,7 @@ private:
 
     // RotGrfFlyFrame: Helper to acces sthe rotation angle (in 10th degrees, left-handed)
     // of a GraphicFrame
-    sal_uInt16 getPossibleRotationFromFraphicFrame() const;
+    sal_uInt16 getPossibleRotationFromFraphicFrame(Size& rSize) const;
 
 protected:
     // AW: Need own sdr::contact::ViewContact since AnchorPos from parent is
@@ -77,6 +77,8 @@ public:
     basegfx::B2DRange getOuterBound() const;
     basegfx::B2DRange getInnerBound() const;
 
+    // RotGrfFlyFrame: Check if this is a SwGrfNode
+    bool ContainsSwGrfNode() const;
 
     SwVirtFlyDrawObj(SdrObject& rNew, SwFlyFrame* pFly);
     virtual ~SwVirtFlyDrawObj() override;
@@ -105,6 +107,7 @@ public:
                                     const Fraction& yFact, bool bUnsetRelative = true) override;
     virtual       void       Crop(const Point& rRef, const Fraction& xFact, const Fraction& yFact) override;
     virtual       void       addCropHandles(SdrHdlList& rTarget) const override;
+    virtual       void       Rotate(const Point& rRef, long nAngle, double sn, double cs) override;
 
     // FullDrag support
     virtual SdrObject* getFullDragClone() const override;
@@ -122,6 +125,9 @@ public:
     virtual bool       HasMacro() const override;
     virtual SdrObject* CheckMacroHit       (const SdrObjMacroHitRec& rRec) const override;
     virtual Pointer    GetMacroPointer     (const SdrObjMacroHitRec& rRec) const override;
+
+    // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation.
+    virtual bool HasLimitedRotation() const override;
 };
 
 #endif
diff --git a/sw/source/uibase/frmdlg/frmmgr.cxx b/sw/source/uibase/frmdlg/frmmgr.cxx
index c73238be62ea..7cd7c2702333 100644
--- a/sw/source/uibase/frmdlg/frmmgr.cxx
+++ b/sw/source/uibase/frmdlg/frmmgr.cxx
@@ -577,9 +577,9 @@ void SwFlyFrameAttrMgr::SetHeightSizeType( SwFrameSize eType )
 void SwFlyFrameAttrMgr::SetRotation(sal_uInt16 nOld, sal_uInt16 nNew, const Size& rUnrotatedSize)
 {
     // RotGrfFlyFrame: Central handling of real change of rotation here, all adaptions use this.
-    // Adaption of pos/size may be wanted in the future. Already tried to keep last SIze in
+    // Adaption of pos/size may be wanted in the future. Already tried to keep last Size in
     // UnrotatedSize in the SwRotationGrf Item, but this will lead to various problems. Also tried
-    // to use m_aSet.Put(...) as in other methods (also read methods for Rotation/UnrotatedSize) but
+    // to use m_aSet.Put(...) as in other methods (also tried read methods for Rotation/UnrotatedSize) but
     // somehow the needed ID (RES_GRFATR_ROTATION) is *not* in the SfxItemSet of the Frame, so for
     // now set directly. Undo/Redo is preserved by AttributeChange
     if(nOld != nNew)
diff --git a/sw/source/uibase/shells/grfsh.cxx b/sw/source/uibase/shells/grfsh.cxx
index 0914846b7857..23857b2b4745 100644
--- a/sw/source/uibase/shells/grfsh.cxx
+++ b/sw/source/uibase/shells/grfsh.cxx
@@ -123,6 +123,27 @@ void SwGrfShell::Execute(SfxRequest &rReq)
     sal_uInt16 nSlot = rReq.GetSlot();
     switch(nSlot)
     {
+        case SID_OBJECT_ROTATE:
+        {
+            // RotGrfFlyFrame: start rotation when possible
+            SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList();
+
+            if(rSh.IsRotationOfSwGrfNodePossible() && pSdrView->IsRotateAllowed())
+            {
+                if(GetView().IsDrawRotate())
+                {
+                    rSh.SetDragMode(SdrDragMode::Move);
+                }
+                else
+                {
+                    rSh.SetDragMode(SdrDragMode::Rotate);
+                }
+
+                GetView().FlipDrawRotate();
+            }
+        }
+        break;
+
         case SID_TWAIN_TRANSFER:
         {
             GetView().ExecuteScan( rReq );
@@ -721,6 +742,23 @@ void SwGrfShell::GetAttrState(SfxItemSet &rSet)
         bool bDisable = bParentCntProt;
         switch( nWhich )
         {
+        case SID_OBJECT_ROTATE:
+        {
+            // RotGrfFlyFrame: steer rotation state
+            const bool bIsRotate(GetView().IsDrawRotate());
+            SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList();
+
+            if(!bIsRotate && !pSdrView->IsRotateAllowed())
+            {
+                rSet.DisableItem(nWhich);
+            }
+            else
+            {
+                rSet.Put(SfxBoolItem(nWhich, bIsRotate));
+            }
+
+            break;
+        }
         case SID_INSERT_GRAPHIC:
         case FN_FORMAT_GRAFIC_DLG:
         case SID_TWAIN_TRANSFER:
diff --git a/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml b/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml
index c1ba2219af1b..6494546daebd 100644
--- a/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml
+++ b/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml
@@ -28,6 +28,7 @@
  <toolbar:toolbaritem xlink:href=".uno:FlipHorizontal"/>
  <toolbar:toolbaritem xlink:href=".uno:RotateLeft"/>
  <toolbar:toolbaritem xlink:href=".uno:RotateRight"/>
+ <toolbar:toolbaritem xlink:href=".uno:ToggleObjectRotateMode"/>
  <toolbar:toolbaritem xlink:href=".uno:Rotate180" toolbar:visible="false"/>
  <toolbar:toolbaritem xlink:href=".uno:RotateReset" toolbar:visible="false"/>
  <toolbar:toolbarseparator/>
commit f3305fc1f6f87bbab6737244a2311c7d1ed51250
Author: Armin Le Grand <Armin.Le.Grand at cib.de>
Date:   Fri Sep 29 12:35:44 2017 +0200

    RotGrfFlyFrame: Corrected position for CropHandles
    
    Position was taken from OuterBound FlyFrame, even in current
    master which is wrong. There can be a distance defined between
    InnerBound and OuterBound that has to be taken into account
    
    Change-Id: Id88f99c0b218bd26fa1daa5e8215eced00c0baa6

diff --git a/sw/source/core/draw/dflyobj.cxx b/sw/source/core/draw/dflyobj.cxx
index 358085bd2760..6a21d0eb9d71 100644
--- a/sw/source/core/draw/dflyobj.cxx
+++ b/sw/source/core/draw/dflyobj.cxx
@@ -48,6 +48,7 @@
 #include "pagefrm.hxx"
 #include "rootfrm.hxx"
 #include "wrtsh.hxx"
+#include <ndgrf.hxx>
 
 #include <svx/sdr/properties/defaultproperties.hxx>
 #include <basegfx/range/b2drange.hxx>
@@ -958,10 +959,10 @@ SdrObject* SwVirtFlyDrawObj::getFullDragClone() const
         if(0 != nRotation)
         {
             const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0));
-            const tools::Rectangle aOutRect(GetFlyFrame()->Frame().SVRect());
+            const tools::Rectangle aTmpOutRect(GetFlyFrame()->Frame().SVRect());
             const basegfx::B2DRange aTargetRange(
-                aOutRect.Left(), aOutRect.Top(),
-                aOutRect.Right(), aOutRect.Bottom());
+                aTmpOutRect.Left(), aTmpOutRect.Top(),
+                aTmpOutRect.Right(), aTmpOutRect.Bottom());
             const basegfx::B2DHomMatrix aTargetTransform(
                 basegfx::utils::createRotateAroundCenterKeepAspectRatioStayInsideRange(
                     aTargetRange,
@@ -979,10 +980,10 @@ 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());
+        // Use InnerBound, OuterBound (same as GetFlyFrame()->Frame().SVRect())
+        // may have a distance to InnerBound which needs to be taken into acocunt.
+        // The Graphic is mapped to InnerBound, as is the rotated Graphic.
+        const basegfx::B2DRange aTargetRange(getInnerBound());
 
         if(!aTargetRange.isEmpty())
         {
diff --git a/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml b/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml
index 172d8b4d52b5..c1ba2219af1b 100644
--- a/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml
+++ b/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml
@@ -29,7 +29,7 @@
  <toolbar:toolbaritem xlink:href=".uno:RotateLeft"/>
  <toolbar:toolbaritem xlink:href=".uno:RotateRight"/>
  <toolbar:toolbaritem xlink:href=".uno:Rotate180" toolbar:visible="false"/>
- <toolbar:toolbaritem xlink:href=".uno:RotateReset"/>
+ <toolbar:toolbaritem xlink:href=".uno:RotateReset" toolbar:visible="false"/>
  <toolbar:toolbarseparator/>
  <toolbar:toolbaritem xlink:href=".uno:GrafTransparence"/>
  <toolbar:toolbarseparator/>
diff --git a/sw/uiconfig/swxform/toolbar/graphicobjectbar.xml b/sw/uiconfig/swxform/toolbar/graphicobjectbar.xml
index 6e2c6a2b4823..19662053bc76 100644
--- a/sw/uiconfig/swxform/toolbar/graphicobjectbar.xml
+++ b/sw/uiconfig/swxform/toolbar/graphicobjectbar.xml
@@ -30,8 +30,8 @@
  <toolbar:toolbaritem xlink:href=".uno:FlipHorizontal"/>
  <toolbar:toolbaritem xlink:href=".uno:RotateLeft"/>
  <toolbar:toolbaritem xlink:href=".uno:RotateRight"/>
- <toolbar:toolbaritem xlink:href=".uno:Rotate180"/>
- <toolbar:toolbaritem xlink:href=".uno:RotateReset"/>
+ <toolbar:toolbaritem xlink:href=".uno:Rotate180" toolbar:visible="false"/>
+ <toolbar:toolbaritem xlink:href=".uno:RotateReset" toolbar:visible="false"/>
  <toolbar:toolbaritem xlink:href=".uno:Crop"/>
  <toolbar:toolbarseparator/>
  <toolbar:toolbaritem xlink:href=".uno:FrameDialog"/>
commit 271bd30b8bc872e2e74af07f7a361a9e9f15ce9d
Author: Armin Le Grand <Armin.Le.Grand at cib.de>
Date:   Fri Sep 29 10:42:43 2017 +0200

    RotGrfFlyFrame: Implemented Handle Update on rotation change
    
    WIth rotaiton being allowed the handle visualisation may be
    dependent on this, so it is necessary to refresh their
    visualisation on rotation chnage, e.g. for crop handles
    
    Change-Id: I218e326894999381fc4058b7eba432491a0cf23b

diff --git a/include/basegfx/matrix/b2dhommatrixtools.hxx b/include/basegfx/matrix/b2dhommatrixtools.hxx
index e462bad54b8e..3bc3627dfbf5 100644
--- a/include/basegfx/matrix/b2dhommatrixtools.hxx
+++ b/include/basegfx/matrix/b2dhommatrixtools.hxx
@@ -229,6 +229,7 @@ namespace basegfx
             const B2DVector& getScale() const { const_cast< B2DHomMatrixBufferedOnDemandDecompose* >(this)->impCheckDecompose(); return maScale; }
             const B2DVector& getTranslate() const { const_cast< B2DHomMatrixBufferedOnDemandDecompose* >(this)->impCheckDecompose(); return maTranslate; }
             double getRotate() const { const_cast< B2DHomMatrixBufferedOnDemandDecompose* >(this)->impCheckDecompose(); return mfRotate; }
+            double getShearX() const { const_cast< B2DHomMatrixBufferedOnDemandDecompose* >(this)->impCheckDecompose(); return mfShearX; }
         };
     } // end of namespace utils
 
diff --git a/svx/source/svdraw/svddrgmt.cxx b/svx/source/svdraw/svddrgmt.cxx
index 72279ec410a8..3348579bd508 100644
--- a/svx/source/svdraw/svddrgmt.cxx
+++ b/svx/source/svdraw/svddrgmt.cxx
@@ -595,14 +595,14 @@ void SdrDragMethod::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
     {
         // do something special since the object size is in the polygon
         // break up matrix to get the scale
-        const basegfx::utils::B2DHomMatrixBufferedDecompose aDecomp(aObjectTransform);
+        const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aObjectTransform);
 
         // get polygon's position and size
         const basegfx::B2DRange aPolyRange(aObjectPolyPolygon.getB2DRange());
 
         // get the scaling factors (do not mirror, this is in the object transformation)
-        const double fScaleX(fabs(aDecomp.getScale().getX()) / (basegfx::fTools::equalZero(aPolyRange.getWidth()) ? 1.0 : aPolyRange.getWidth()));
-        const double fScaleY(fabs(aDecomp.getScale().getY()) / (basegfx::fTools::equalZero(aPolyRange.getHeight()) ? 1.0 : aPolyRange.getHeight()));
+        const double fScaleX(fabs(aTmpDecomp.getScale().getX()) / (basegfx::fTools::equalZero(aPolyRange.getWidth()) ? 1.0 : aPolyRange.getWidth()));
+        const double fScaleY(fabs(aTmpDecomp.getScale().getY()) / (basegfx::fTools::equalZero(aPolyRange.getHeight()) ? 1.0 : aPolyRange.getHeight()));
 
         // prepare transform matrix for polygon
         basegfx::B2DHomMatrix aPolyTransform(
@@ -3657,16 +3657,16 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
     pObj->TRGetBaseGeometry(aOriginalMatrix, aPolyPolygon);
 
     {   // correct shear, it comes currently mirrored from TRGetBaseGeometry, can be removed with aw080
-        const basegfx::utils::B2DHomMatrixBufferedDecompose aDecomp(aOriginalMatrix);
+        const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aOriginalMatrix);
 
-        if(!basegfx::fTools::equalZero(aDecomp.getShearX()))
+        if(!basegfx::fTools::equalZero(aTmpDecomp.getShearX()))
         {
             bShearCorrected = true;
             aOriginalMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
-                aDecomp.getScale(),
-                -aDecomp.getShearX(),
-                aDecomp.getRotate(),
-                aDecomp.getTranslate());
+                aTmpDecomp.getScale(),
+                -aTmpDecomp.getShearX(),
+                aTmpDecomp.getRotate(),
+                aTmpDecomp.getTranslate());
         }
     }
 
@@ -3740,13 +3740,13 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
     // aDiscreteChangeMatrix, go to concrete sizes now.
     // Create the unrotated original rectangle and the unrotated modified
     // rectangle as Ranges
-    const basegfx::utils::B2DHomMatrixBufferedDecompose aDecomp(aOriginalMatrix);
+    const basegfx::utils::B2DHomMatrixBufferedDecompose aOriginalMatrixDecomp(aOriginalMatrix);
 
     // prepare unsheared/unrotated versions of the old and new transformation
     const basegfx::B2DHomMatrix aOriginalMatrixNoShearNoRotate(
         basegfx::utils::createScaleTranslateB2DHomMatrix(
-            basegfx::absolute(aDecomp.getScale()),
-            aDecomp.getTranslate()));
+            basegfx::absolute(aOriginalMatrixDecomp.getScale()),
+            aOriginalMatrixDecomp.getTranslate()));
 
     // create the ranges for these
     basegfx::B2DRange aRangeOriginalNoShearNoRotate(0.0, 0.0, 1.0, 1.0);
@@ -3784,13 +3784,13 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
         if(bShearCorrected)
         {
             // back-correct shear
-            const basegfx::utils::B2DHomMatrixBufferedDecompose aDecomp(aNewObjectMatrix);
+            const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aNewObjectMatrix);
 
             aNewObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
-                aDecomp.getScale(),
-                -aDecomp.getShearX(),
-                aDecomp.getRotate(),
-                aDecomp.getTranslate());
+                aTmpDecomp.getScale(),
+                -aTmpDecomp.getShearX(),
+                aTmpDecomp.getRotate(),
+                aTmpDecomp.getTranslate());
         }
 
         // apply change to object by applying the unit coordinate change followed
diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx
index 6a23088246c4..a1154b2977d5 100644
--- a/sw/source/core/doc/notxtfrm.cxx
+++ b/sw/source/core/doc/notxtfrm.cxx
@@ -626,6 +626,27 @@ void SwNoTextFrame::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew )
                                 GetItemState( n, false ))
                 {
                     CLEARCACHE
+
+                    if(RES_GRFATR_ROTATION == n)
+                    {
+                        // RotGrfFlyFrame: Update Handles in view, these may be rotation-dependent
+                        // (e.g. crop handles) and need a visualisation update
+                        if ( GetNode()->GetNodeType() == SwNodeType::Grf )
+                        {
+                            SwGrfNode* pNd = static_cast<SwGrfNode*>( GetNode());
+                            SwViewShell *pVSh = pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+
+                            if(pVSh)
+                            {
+                                SdrView* pDrawView = pVSh->GetDrawView();
+
+                                if(pDrawView)
+                                {
+                                    pDrawView->AdjustMarkHdl(nullptr);
+                                }
+                            }
+                        }
+                    }
                     break;
                 }
             if( RES_GRFATR_END == n )           // not found
commit 18c27870e99263945bd3df78ecce5f2aa158e850
Author: Armin Le Grand <Armin.Le.Grand at cib.de>
Date:   Thu Sep 28 13:08:35 2017 +0200

    RotGrfFlyFrame: Encapsulated matrix decomposes
    
    Change-Id: I432766970ef06c7d85a0056d3f24280cf2acd47d

diff --git a/svx/source/svdraw/svddrgmt.cxx b/svx/source/svdraw/svddrgmt.cxx
index 90a6ad41f452..72279ec410a8 100644
--- a/svx/source/svdraw/svddrgmt.cxx
+++ b/svx/source/svdraw/svddrgmt.cxx
@@ -595,21 +595,20 @@ void SdrDragMethod::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
     {
         // do something special since the object size is in the polygon
         // break up matrix to get the scale
-        basegfx::B2DTuple aScale;
-        basegfx::B2DTuple aTranslate;
-        double fRotate, fShearX;
-        aObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
+        const basegfx::utils::B2DHomMatrixBufferedDecompose aDecomp(aObjectTransform);
 
         // get polygon's position and size
         const basegfx::B2DRange aPolyRange(aObjectPolyPolygon.getB2DRange());
 
         // get the scaling factors (do not mirror, this is in the object transformation)
-        const double fScaleX(fabs(aScale.getX()) / (basegfx::fTools::equalZero(aPolyRange.getWidth()) ? 1.0 : aPolyRange.getWidth()));
-        const double fScaleY(fabs(aScale.getY()) / (basegfx::fTools::equalZero(aPolyRange.getHeight()) ? 1.0 : aPolyRange.getHeight()));
+        const double fScaleX(fabs(aDecomp.getScale().getX()) / (basegfx::fTools::equalZero(aPolyRange.getWidth()) ? 1.0 : aPolyRange.getWidth()));
+        const double fScaleY(fabs(aDecomp.getScale().getY()) / (basegfx::fTools::equalZero(aPolyRange.getHeight()) ? 1.0 : aPolyRange.getHeight()));
 
         // prepare transform matrix for polygon
-        basegfx::B2DHomMatrix aPolyTransform(basegfx::utils::createTranslateB2DHomMatrix(
-            -aPolyRange.getMinX(), -aPolyRange.getMinY()));
+        basegfx::B2DHomMatrix aPolyTransform(
+            basegfx::utils::createTranslateB2DHomMatrix(
+                -aPolyRange.getMinX(),
+                -aPolyRange.getMinY()));
         aPolyTransform.scale(fScaleX, fScaleY);
 
         // transform the polygon
@@ -3658,18 +3657,16 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
     pObj->TRGetBaseGeometry(aOriginalMatrix, aPolyPolygon);
 
     {   // correct shear, it comes currently mirrored from TRGetBaseGeometry, can be removed with aw080
-        basegfx::B2DTuple aScale, aTranslate;
-        double fRotate(0.0), fShearX(0.0);
-        aOriginalMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+        const basegfx::utils::B2DHomMatrixBufferedDecompose aDecomp(aOriginalMatrix);
 
-        if(!basegfx::fTools::equalZero(fShearX))
+        if(!basegfx::fTools::equalZero(aDecomp.getShearX()))
         {
             bShearCorrected = true;
             aOriginalMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
-                aScale,
-                -fShearX,
-                fRotate,
-                aTranslate);
+                aDecomp.getScale(),
+                -aDecomp.getShearX(),
+                aDecomp.getRotate(),
+                aDecomp.getTranslate());
         }
     }
 
@@ -3743,17 +3740,13 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
     // 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);
+    const basegfx::utils::B2DHomMatrixBufferedDecompose aDecomp(aOriginalMatrix);
 
     // prepare unsheared/unrotated versions of the old and new transformation
     const basegfx::B2DHomMatrix aOriginalMatrixNoShearNoRotate(
         basegfx::utils::createScaleTranslateB2DHomMatrix(
-            basegfx::absolute(aScale),
-            aTranslate));
+            basegfx::absolute(aDecomp.getScale()),
+            aDecomp.getTranslate()));
 
     // create the ranges for these
     basegfx::B2DRange aRangeOriginalNoShearNoRotate(0.0, 0.0, 1.0, 1.0);
@@ -3791,16 +3784,13 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
         if(bShearCorrected)
         {
             // back-correct shear
-            basegfx::B2DTuple aScale;
-            basegfx::B2DTuple aTranslate;
-            double fRotate(0.0), fShearX(0.0);
+            const basegfx::utils::B2DHomMatrixBufferedDecompose aDecomp(aNewObjectMatrix);
 
-            aNewObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
             aNewObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
-                aScale,
-                -fShearX,
-                fRotate,
-                aTranslate);
+                aDecomp.getScale(),
+                -aDecomp.getShearX(),
+                aDecomp.getRotate(),
+                aDecomp.getTranslate());
         }
 
         // apply change to object by applying the unit coordinate change followed
commit c3505f97fa522df46aad58594b7932f1ff0a3b2f
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 a267b05b96d1..26936aff2e5c 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 c60a6ac96c21..e462bad54b8e 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 aed40a9f13d1..90a6ad41f452 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::utils::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::utils::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::utils::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::utils::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 42eac79c45ee..6a23088246c4 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::utils::createScaleTranslateB2DHomMatrix(
-            aTargetRange.getRange(),
-            aTargetRange.getMinimum());
-    }
-
+    const double fRotate(static_cast< double >(-rGraphicAttr.GetRotation()) * (M_PI/1800.0));
+    const basegfx::B2DHomMatrix aTargetTransform(
+        basegfx::utils::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 0696af0e59d1..358085bd2760 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::utils::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::utils::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 305c8ffb5495..f0351fa6de29 100644
--- a/sw/source/core/inc/frmtool.hxx
+++ b/sw/source/core/inc/frmtool.hxx
@@ -72,10 +72,11 @@ bool DrawFillAttributes(
     const basegfx::utils::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
commit 0d14a998d688debe086827cf69dae99a7db7593e
Author: Armin Le Grand <Armin.Le.Grand at cib.de>
Date:   Tue Sep 26 15:41:23 2017 +0200

    RotGrfFlyFrame: Minimal working rotation solution
    
    This version allows rotation (in 10th degrees) and perserves
    it over save/load cycles. Rotation of multiples of 90 degree
    behave close to original except not changing the contained
    Graphic and being adaptable to all kinds of graphic. The
    rotated Graphic is displayed centered and under preserved
    AspectRatio in the available frame space (so no rotation,
    180 degree is identical, 90/-90 is identical with 1:1 ratio
    of the graphic)
    
    Change-Id: I54b3385f709ee0d34a55324aca919dcd2ce0c009

diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx
index a35f679d10be..42eac79c45ee 100644
--- a/sw/source/core/doc/notxtfrm.cxx
+++ b/sw/source/core/doc/notxtfrm.cxx
@@ -773,16 +773,36 @@ void paintGraphicUsingPrimitivesHelper(vcl::RenderContext & rOutputDevice,
     // 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.translate(-0.5, -0.5);
+        aTargetTransform.scale(1.0, aTargetRange.getHeight() / aTargetRange.getWidth());
         aTargetTransform.rotate(fRotate);
-        aTargetTransform.translate(0.5, 0.5);
-    }
 
-    // needed scale/translate
-    aTargetTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(
-        aTargetRange.getRange(),
-        aTargetRange.getMinimum());
+        // 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::utils::createScaleTranslateB2DHomMatrix(
+            aTargetRange.getRange(),
+            aTargetRange.getMinimum());
+    }
 
     drawinglayer::primitive2d::Primitive2DContainer aContent(1);
     bool bDone(false);
diff --git a/sw/source/uibase/frmdlg/frmmgr.cxx b/sw/source/uibase/frmdlg/frmmgr.cxx
index fdc883b6a22a..c73238be62ea 100644
--- a/sw/source/uibase/frmdlg/frmmgr.cxx
+++ b/sw/source/uibase/frmdlg/frmmgr.cxx
@@ -46,10 +46,15 @@
 using namespace ::com::sun::star;
 
 static sal_uInt16 aFrameMgrRange[] = {
-                            RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+                            RES_FRMATR_BEGIN, RES_FRMATR_END-1, // 87-129
+
+                            // RotGrfFlyFrame: Support here, but seems not to be
+                            // added in range of m_pOwnSh->GetFlyFrameAttr result
+                            // (see below). Tried to find, but could not identify
+                            RES_GRFATR_ROTATION, RES_GRFATR_ROTATION, // 132
 
                             // FillAttribute support
-                            XATTR_FILL_FIRST, XATTR_FILL_LAST,
+                            XATTR_FILL_FIRST, XATTR_FILL_LAST, // 1014-1033
 
                             SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER,
                             FN_SET_FRM_NAME, FN_SET_FRM_NAME,
@@ -569,13 +574,17 @@ void SwFlyFrameAttrMgr::SetHeightSizeType( SwFrameSize eType )
     m_aSet.Put( aSize );
 }
 
-void SwFlyFrameAttrMgr::SetRotation(sal_uInt32 nOld, sal_uInt32 nNew, Size aUnrotatedSize)
+void SwFlyFrameAttrMgr::SetRotation(sal_uInt16 nOld, sal_uInt16 nNew, const Size& rUnrotatedSize)
 {
-    // RotGrfFlyFrame: Central handling of real change of rotation here. Adaption of pos/size
-    // may be wanted in the future
+    // RotGrfFlyFrame: Central handling of real change of rotation here, all adaptions use this.
+    // Adaption of pos/size may be wanted in the future. Already tried to keep last SIze in
+    // UnrotatedSize in the SwRotationGrf Item, but this will lead to various problems. Also tried
+    // to use m_aSet.Put(...) as in other methods (also read methods for Rotation/UnrotatedSize) but
+    // somehow the needed ID (RES_GRFATR_ROTATION) is *not* in the SfxItemSet of the Frame, so for
+    // now set directly. Undo/Redo is preserved by AttributeChange
     if(nOld != nNew)
     {
-        m_pOwnSh->SetAttrItem(SwRotationGrf(static_cast<sal_uInt16>(nNew), aUnrotatedSize));
+        m_pOwnSh->SetAttrItem(SwRotationGrf(nNew, rUnrotatedSize));
     }
 }
 
diff --git a/sw/source/uibase/inc/frmmgr.hxx b/sw/source/uibase/inc/frmmgr.hxx
index cdc3680ab894..e0f939ff2223 100644
--- a/sw/source/uibase/inc/frmmgr.hxx
+++ b/sw/source/uibase/inc/frmmgr.hxx
@@ -93,7 +93,7 @@ public:
     void                SetHeightSizeType(SwFrameSize eType);
 
     // rotation
-    void                SetRotation(sal_uInt32 nOld, sal_uInt32 nNew, Size aUnrotatedSize);
+    void                SetRotation(sal_uInt16 nOld, sal_uInt16 nNew, const Size& rUnrotatedSize);
 
     // space to content
     void                SetLRSpace( long nLeft,
diff --git a/sw/source/uibase/shells/grfsh.cxx b/sw/source/uibase/shells/grfsh.cxx
index 7b2e034ae670..0914846b7857 100644
--- a/sw/source/uibase/shells/grfsh.cxx
+++ b/sw/source/uibase/shells/grfsh.cxx
@@ -893,12 +893,6 @@ void SwGrfShell::GetAttrState(SfxItemSet &rSet)
 void SwGrfShell::ExecuteRotation(SfxRequest const &rReq)
 {
     // RotGrfFlyFrame: Modify rotation attribute instead of manipulating the graphic
-    SwWrtShell& rShell = GetShell();
-    SfxItemSet aSet( rShell.GetAttrPool(), svl::Items<
-        RES_GRFATR_ROTATION, RES_GRFATR_ROTATION,
-        SID_ATTR_TRANSFORM_ANGLE, SID_ATTR_TRANSFORM_ANGLE>{} );
-    rShell.GetCurAttr( aSet );
-    const SwRotationGrf& rRotation = static_cast<const SwRotationGrf&>(aSet.Get(RES_GRFATR_ROTATION));
     sal_uInt16 aRotation(0);
 
     if (rReq.GetSlot() == SID_ROTATE_GRAPHIC_LEFT)
@@ -916,22 +910,23 @@ void SwGrfShell::ExecuteRotation(SfxRequest const &rReq)
 
     if (rReq.GetSlot() == SID_ROTATE_GRAPHIC_RESET || 0 != aRotation)
     {
-        rShell.StartAllAction();
-        rShell.StartUndo(SwUndoId::START);
+        SwWrtShell& rShell = GetShell();
+        SfxItemSet aSet( rShell.GetAttrPool(), svl::Items<RES_GRFATR_ROTATION, RES_GRFATR_ROTATION>{} );
+        rShell.GetCurAttr( aSet );
+        const SwRotationGrf& rRotation = static_cast<const SwRotationGrf&>(aSet.Get(RES_GRFATR_ROTATION));
+        SwFlyFrameAttrMgr aMgr(false, &rShell, rShell.IsFrameSelected() ? Frmmgr_Type::NONE : Frmmgr_Type::GRF);
 
+        // RotGrfFlyFrame: Possible rotation change here, SwFlyFrameAttrMgr aMgr is available
         if (rReq.GetSlot() == SID_ROTATE_GRAPHIC_RESET)
         {
-            rShell.SetAttrItem(SwRotationGrf(0, rRotation.GetUnrotatedSize()));
+            aMgr.SetRotation(rRotation.GetValue(), 0, rRotation.GetUnrotatedSize());
         }
         else if(0 != aRotation)
         {
-            sal_uInt16 aNewRotation((aRotation + rRotation.GetValue()) % 3600);
+            const sal_uInt16 aNewRotation((aRotation + rRotation.GetValue()) % 3600);
 
-            rShell.SetAttrItem(SwRotationGrf(aNewRotation, rRotation.GetUnrotatedSize()));
+            aMgr.SetRotation(rRotation.GetValue(), aNewRotation, rRotation.GetUnrotatedSize());
         }
-
-        rShell.EndUndo(SwUndoId::END);
-        rShell.EndAllAction();
     }
 }
 
commit 0869e0ee03c0bc40b07bff76ba29476c6cc25435
Author: Armin Le Grand <Armin.Le.Grand at cib.de>
Date:   Fri Sep 22 15:28:33 2017 +0200

    RotGrfFlyFrame: Initial adaptions
    
    To allow free rotation of Graphic FlyFrames in Writer,
    several adaptions are necessary. This change takes care
    of all needed changes to internally support a freely
    definable rotation angle for that case. Save/Load round
    trip is working, the graphic does no longer get modified
    and added in 90-degree-changed state to the object, the
    original will be preserved. Support for needed slot in
    core/ui is implemented. Rotation can be applied from
    Menus/Toolbars in the known 90/180 degree steps. Added
    a slot/Button/command to reset rotation in these cases.
    Added support in Sidebar to rotate using the rotation
    wheel and/or numeric field. These fields and support added
    to Image TabPage, too, fully functional.
    Missing now is a solution for displaying the rotated
    Graphic. For now, it just gets rotated, but this will not
    be the final state of this change.
    
    Change-Id: I6f3b85ebb5be2b4ad3311c536d54f27a37a494e7
    
    RotGrfFlyFrame: Linux build adaptions
    
    Change-Id: I365287ecd6525b1972e8436d61332f7121d88649

diff --git a/include/svx/svxids.hrc b/include/svx/svxids.hrc
index 84103287e776..79ebc50726c1 100644
--- a/include/svx/svxids.hrc
+++ b/include/svx/svxids.hrc
@@ -919,7 +919,7 @@
 #define SID_ROTATE_GRAPHIC_LEFT                         ( SID_SVX_START + 1121 )
 #define SID_ROTATE_GRAPHIC_RIGHT                        ( SID_SVX_START + 1122 )
 #define SID_ROTATE_GRAPHIC_180                          ( SID_SVX_START + 1123 )
-
+#define SID_ROTATE_GRAPHIC_RESET                        ( SID_SVX_START + 1092 ) /* RotGrfFlyFrame: new slot */
 
 // new slots for panels
 #define SID_ATTR_FILL_TRANSPARENCE                      ( SID_SVX_START + 1124 )
diff --git a/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu
index 5803979d41f8..52fec078a080 100644
--- a/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu
+++ b/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu
@@ -983,6 +983,14 @@
           <value>1</value>
         </prop>
       </node>
+      <node oor:name=".uno:RotateReset" oor:op="replace">
+        <prop oor:name="Label" oor:type="xs:string">
+          <value xml:lang="en-US">Reset R~otation</value>
+        </prop>
+        <prop oor:name="Properties" oor:type="xs:int">
+          <value>1</value>
+        </prop>
+      </node>
       <node oor:name=".uno:NewHtmlDoc" oor:op="replace">
         <prop oor:name="Label" oor:type="xs:string">
           <value xml:lang="en-US">Create ~HTML Document</value>
diff --git a/svx/sdi/svx.sdi b/svx/sdi/svx.sdi
index 932aef3cb2c2..acd7f4c91f6b 100644
--- a/svx/sdi/svx.sdi
+++ b/svx/sdi/svx.sdi
@@ -4305,6 +4305,23 @@ SfxVoidItem RotateRight SID_ROTATE_GRAPHIC_RIGHT
     GroupId = SfxGroupId::Graphic;
 ]
 
+SfxVoidItem RotateReset SID_ROTATE_GRAPHIC_RESET
+
+[
+    AutoUpdate = FALSE,
+    FastCall = FALSE,
+    ReadOnlyDoc = TRUE,
+    Toggle = FALSE,
+    Container = FALSE,
+    RecordAbsolute = FALSE,
+    RecordPerSet;
+
+    AccelConfig = TRUE,
+    MenuConfig = TRUE,
+    ToolBoxConfig = TRUE,
+    GroupId = SfxGroupId::Graphic;
+]
+
 SfxBoolItem Crop SID_OBJECT_CROP
 ()
 [
diff --git a/svx/source/sidebar/possize/PosSizePropertyPanel.cxx b/svx/source/sidebar/possize/PosSizePropertyPanel.cxx
index ab27b80cdc68..3895b2a41208 100644
--- a/svx/source/sidebar/possize/PosSizePropertyPanel.cxx
+++ b/svx/source/sidebar/possize/PosSizePropertyPanel.cxx
@@ -270,6 +270,7 @@ void PosSizePropertyPanel::HandleContextChange(
 
         case CombinedEnumContext(Application::WriterVariants, Context::Graphic):
             bShowFlip = true;
+            bShowAngle = true; // RotGrfFlyFrame: Writer FlyFrames for Graphics now support angle
             break;
 
         case CombinedEnumContext(Application::Calc, Context::Draw):
diff --git a/sw/sdi/_grfsh.sdi b/sw/sdi/_grfsh.sdi
index 1dc453608103..8291745966bb 100644
--- a/sw/sdi/_grfsh.sdi
+++ b/sw/sdi/_grfsh.sdi
@@ -94,6 +94,20 @@ interface BaseTextGraphic
         DisableFlags="SfxDisableFlags::SwOnProtectedCursor";
     ]
 
+    SID_ROTATE_GRAPHIC_RESET
+    [
+        ExecMethod = ExecuteRotation ;
+        StateMethod = GetAttrStateForRotation ;
+        DisableFlags="SfxDisableFlags::SwOnProtectedCursor";
+    ]
+
+    SID_ATTR_TRANSFORM_ANGLE
+    [
+        ExecMethod = ExecuteRotation ;
+        StateMethod = GetAttrStateForRotation ;
+        DisableFlags="SfxDisableFlags::SwOnProtectedCursor";
+    ]
+
     SID_OBJECT_CROP
     [
         ExecMethod = Execute ;
diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx
index c4791e086d4b..a35f679d10be 100644
--- a/sw/source/core/doc/notxtfrm.cxx
+++ b/sw/source/core/doc/notxtfrm.cxx
@@ -768,10 +768,21 @@ void paintGraphicUsingPrimitivesHelper(vcl::RenderContext & rOutputDevice,
     const basegfx::B2DRange aTargetRange(
         rAlignedGrfArea.Left(), rAlignedGrfArea.Top(),
         rAlignedGrfArea.Right(), rAlignedGrfArea.Bottom());
-    const basegfx::B2DHomMatrix aTargetTransform(
-        basegfx::utils::createScaleTranslateB2DHomMatrix(
-            aTargetRange.getRange(),
-            aTargetRange.getMinimum()));
+    basegfx::B2DHomMatrix aTargetTransform;
+
+    // RotGrfFlyFrame: Take rotation into account. Rotation is in 10th degrees
+    if(0 != rGraphicAttr.GetRotation())
+    {
+        const double fRotate(static_cast< double >(-rGraphicAttr.GetRotation()) * (M_PI/1800.0));
+        aTargetTransform.translate(-0.5, -0.5);
+        aTargetTransform.rotate(fRotate);
+        aTargetTransform.translate(0.5, 0.5);
+    }
+
+    // needed scale/translate
+    aTargetTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(
+        aTargetRange.getRange(),
+        aTargetRange.getMinimum());
 
     drawinglayer::primitive2d::Primitive2DContainer aContent(1);
     bool bDone(false);
diff --git a/sw/source/ui/frmdlg/frmpage.cxx b/sw/source/ui/frmdlg/frmpage.cxx
index e36595431cee..f1abe60c19fe 100644
--- a/sw/source/ui/frmdlg/frmpage.cxx
+++ b/sw/source/ui/frmdlg/frmpage.cxx
@@ -2388,6 +2388,12 @@ SwGrfExtPage::SwGrfExtPage(vcl::Window *pParent, const SfxItemSet &rSet)
     get(m_pBmpWin, "preview");
     m_pBmpWin->SetBitmapEx(get<FixedImage>("fallback")->GetImage().GetBitmapEx());
 
+    // RotGrfFlyFrame: Need Angle and RotateControls now
+    get(m_pFlAngle, "FL_ANGLE");
+    get(m_pNfAngle, "NF_ANGLE");
+    get(m_pCtlAngle, "CTL_ANGLE");
+    m_pCtlAngle->SetLinkedField( m_pNfAngle, 2 );
+
     SetExchangeSupport();
     m_pMirrorHorzBox->SetClickHdl( LINK(this, SwGrfExtPage, MirrorHdl));
     m_pMirrorVertBox->SetClickHdl( LINK(this, SwGrfExtPage, MirrorHdl));
@@ -2411,6 +2417,12 @@ void SwGrfExtPage::dispose()
     m_pBmpWin.clear();
     m_pConnectED.clear();
     m_pBrowseBT.clear();
+
+    // RotGrfFlyFrame: Support RotationAngle
+    m_pFlAngle.clear();
+    m_pNfAngle.clear();
+    m_pCtlAngle.clear();
+
     SfxTabPage::dispose();
 }
 
@@ -2432,6 +2444,17 @@ void SwGrfExtPage::Reset(const SfxItemSet *rSet)
         m_pConnectED->SetReadOnly(false);
     }
 
+    // RotGrfFlyFrame: Get RotationAngle and set at control
+    if(SfxItemState::SET == rSet->GetItemState( SID_ATTR_TRANSFORM_ANGLE, false, &pItem))
+    {
+        m_pCtlAngle->SetRotation(static_cast<const SfxInt32Item*>(pItem)->GetValue());
+    }
+    else
+    {
+        m_pCtlAngle->SetRotation(0);
+    }
+    m_pCtlAngle->SaveValue();
+
     ActivatePage(*rSet);
 }
 
@@ -2559,6 +2582,14 @@ bool SwGrfExtPage::FillItemSet( SfxItemSet *rSet )
         rSet->Put( SvxBrushItem( aGrfName, aFilterName, GPOS_LT,
                                 SID_ATTR_GRAF_GRAPHIC ));
     }
+
+    // RotGrfFlyFrame: Safe rotation if modified
+    if(m_pCtlAngle->IsValueModified())
+    {
+        rSet->Put(SfxInt32Item(GetWhich(SID_ATTR_TRANSFORM_ANGLE), m_pCtlAngle->GetRotation()));
+        bModified = true;
+    }
+
     return bModified;
 }
 
diff --git a/sw/source/uibase/frmdlg/frmmgr.cxx b/sw/source/uibase/frmdlg/frmmgr.cxx
index 73df037c9fdf..fdc883b6a22a 100644
--- a/sw/source/uibase/frmdlg/frmmgr.cxx
+++ b/sw/source/uibase/frmdlg/frmmgr.cxx
@@ -41,6 +41,7 @@
 #include <com/sun/star/text/HoriOrientation.hpp>
 #include <com/sun/star/text/VertOrientation.hpp>
 #include <com/sun/star/text/RelOrientation.hpp>
+#include <grfatr.hxx>
 
 using namespace ::com::sun::star;
 

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list