/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include "svddrgm1.hxx"
#include <math.h>
 
#include <o3tl/numeric.hxx>
#include <osl/diagnose.h>
#include <utility>
#include <vcl/canvastools.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/ptrstyle.hxx>
#include <svx/xpoly.hxx>
#include <svx/svdtrans.hxx>
#include <svx/svdundo.hxx>
#include <svx/svdmark.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svddrgv.hxx>
#include <svx/svdograf.hxx>
#include <svx/strings.hrc>
#include <svx/dialmgr.hxx>
#include <svx/sdgcpitm.hxx>
#include <svx/sdooitm.hxx>
#include <svx/sdtagitm.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <svx/sdr/overlay/overlaymanager.hxx>
#include <sdr/overlay/overlayrollingrectangle.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <svx/sdr/contact/objectcontact.hxx>
#include <svx/svditer.hxx>
#include <svx/svdopath.hxx>
#include <svx/polypolygoneditor.hxx>
#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
#include <sdr/primitive2d/sdrattributecreator.hxx>
#include <sdr/primitive2d/sdrdecompositiontools.hxx>
#include <sdr/primitive2d/sdrprimitivetools.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/attribute/sdrlineattribute.hxx>
#include <drawinglayer/attribute/sdrlinestartendattribute.hxx>
#include <svl/itempool.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <officecfg/Office/Common.hxx>
#include <comphelper/lok.hxx>
#include <map>
#include <vector>
 
 
SdrDragEntry::SdrDragEntry()
:   mbAddToTransparent(false)
{
}
 
SdrDragEntry::~SdrDragEntry()
{
}
 
 
SdrDragEntryPolyPolygon::SdrDragEntryPolyPolygon(basegfx::B2DPolyPolygon aOriginalPolyPolygon)
:   maOriginalPolyPolygon(std::move(aOriginalPolyPolygon))
{
}
 
SdrDragEntryPolyPolygon::~SdrDragEntryPolyPolygon()
{
}
 
drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPolyPolygon::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod, bool IsDragSizeValid)
{
    drawinglayer::primitive2d::Primitive2DContainer aRetval;
 
    if(maOriginalPolyPolygon.count())
    {
        basegfx::B2DPolyPolygon aCopy(maOriginalPolyPolygon);
 
        rDragMethod.applyCurrentTransformationToPolyPolygon(aCopy);
        basegfx::BColor aColA(SvtOptionsDrawinglayer::GetStripeColorA().getBColor());
        basegfx::BColor aColB(SvtOptionsDrawinglayer::GetStripeColorB().getBColor());
        const double fStripeLength(officecfg::Office::Common::Drawinglayer::StripeLength::get());
 
        if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
        {
            aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor();
            aColB.invert();
        }
 
        aRetval.resize(2);
        aRetval[0] = new drawinglayer::primitive2d::PolyPolygonMarkerPrimitive2D(
            aCopy,
            aColA,
            aColB,
            fStripeLength);
 
        basegfx::BColor aHilightColor;
        if (IsDragSizeValid)
            aHilightColor = SvtOptionsDrawinglayer::getHilightColor().getBColor();
        else
            aHilightColor = basegfx::BColor(1.0, 0, 0);
 
        const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
        aRetval[1] = new drawinglayer::primitive2d::PolyPolygonSelectionPrimitive2D(
            std::move(aCopy),
            aHilightColor,
            fTransparence,
            3.0,
            false);
    }
 
    return aRetval;
}
 
 
SdrDragEntrySdrObject::SdrDragEntrySdrObject(
    const SdrObject& rOriginal,
    bool bModify)
:   maOriginal(rOriginal),
    mbModify(bModify)
{
    // add SdrObject parts to transparent overlay stuff
    setAddToTransparent(true);
}
 
SdrDragEntrySdrObject::~SdrDragEntrySdrObject()
{
}
 
void SdrDragEntrySdrObject::prepareCurrentState(SdrDragMethod& rDragMethod)
{
    // for the moment, i need to re-create the clone in all cases. I need to figure
    // out when clone and original have the same class, so that i can use operator=
    // in those cases
 
    mxClone.clear();
 
    if(mbModify)
    {
        mxClone = maOriginal.getFullDragClone();
 
        // apply original transformation, implemented at the DragMethods
        rDragMethod.applyCurrentTransformationToSdrObject(*mxClone);
    }
}
 
drawinglayer::primitive2d::Primitive2DContainer SdrDragEntrySdrObject::createPrimitive2DSequenceInCurrentState(SdrDragMethod&, bool /* IsDragSizeValid */)
{
    const SdrObject* pSource = &maOriginal;
 
    if(mbModify && mxClone)
    {
        // choose source for geometry data
        pSource = mxClone.get();
    }
 
    // use the view-independent primitive representation (without
    // evtl. GridOffset, that may be applied to the DragEntry individually)
    drawinglayer::primitive2d::Primitive2DContainer xRetval;
    pSource->GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
    return xRetval;
}
 
 
SdrDragEntryPrimitive2DSequence::SdrDragEntryPrimitive2DSequence(
    drawinglayer::primitive2d::Primitive2DContainer&& rSequence)
:   maPrimitive2DSequence(std::move(rSequence))
{
    // add parts to transparent overlay stuff if necessary
    setAddToTransparent(true);
}
 
SdrDragEntryPrimitive2DSequence::~SdrDragEntryPrimitive2DSequence()
{
}
 
drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPrimitive2DSequence::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod, bool /* IsDragSizeValid */)
{
    return drawinglayer::primitive2d::Primitive2DContainer {
            new drawinglayer::primitive2d::TransformPrimitive2D(
                rDragMethod.getCurrentTransformation(),
                drawinglayer::primitive2d::Primitive2DContainer(maPrimitive2DSequence))
    };
}
 
SdrDragEntryPointGlueDrag::SdrDragEntryPointGlueDrag(std::vector< basegfx::B2DPoint >&& rPositions, bool bIsPointDrag)
:   maPositions(std::move(rPositions)),
    mbIsPointDrag(bIsPointDrag)
{
    // add SdrObject parts to transparent overlay stuff
    setAddToTransparent(true);
}
 
SdrDragEntryPointGlueDrag::~SdrDragEntryPointGlueDrag()
{
}
 
drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPointGlueDrag::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod, bool /* IsDragSizeValid */)
{
    drawinglayer::primitive2d::Primitive2DContainer aRetval;
 
    if(!maPositions.empty())
    {
        basegfx::B2DPolygon aPolygon;
 
        for(auto const & a: maPositions)
        {
            aPolygon.append(a);
        }
 
        basegfx::B2DPolyPolygon aPolyPolygon(aPolygon);
 
        rDragMethod.applyCurrentTransformationToPolyPolygon(aPolyPolygon);
 
        const basegfx::B2DPolygon aTransformed(aPolyPolygon.getB2DPolygon(0));
        std::vector< basegfx::B2DPoint > aTransformedPositions;
 
        aTransformedPositions.reserve(aTransformed.count());
 
        for(sal_uInt32 a = 0; a < aTransformed.count(); a++)
        {
            aTransformedPositions.push_back(aTransformed.getB2DPoint(a));
        }
 
        if(mbIsPointDrag)
        {
            basegfx::BColor aColor(SvtOptionsDrawinglayer::GetStripeColorA().getBColor());
 
            if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
            {
                aColor = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor();
            }
 
            aRetval = drawinglayer::primitive2d::Primitive2DContainer {
                    new drawinglayer::primitive2d::MarkerArrayPrimitive2D(std::move(aTransformedPositions),
                        drawinglayer::primitive2d::createDefaultCross_3x3(aColor))
            };
        }
        else
        {
            aRetval = drawinglayer::primitive2d::Primitive2DContainer {
                    new drawinglayer::primitive2d::MarkerArrayPrimitive2D(std::move(aTransformedPositions),
                                                                          SdrHdl::createGluePointBitmap())
             };
        }
    }
 
    return aRetval;
}
 
 
void SdrDragMethod::resetSdrDragEntries()
{
    // clear entries; creation is on demand
    clearSdrDragEntries();
}
 
basegfx::B2DRange SdrDragMethod::getCurrentRange() const
{
    return maOverlayObjectList.getBaseRange();
}
 
void SdrDragMethod::clearSdrDragEntries()
{
    maSdrDragEntries.clear();
}
 
void SdrDragMethod::addSdrDragEntry(std::unique_ptr<SdrDragEntry> pNew)
{
    assert(pNew);
    maSdrDragEntries.push_back(std::move(pNew));
}
 
void SdrDragMethod::createSdrDragEntries()
{
    if(!(getSdrDragView().GetSdrPageView() && getSdrDragView().GetSdrPageView()->HasMarkedObjPageView()))
        return;
 
    if(getSdrDragView().IsDraggingPoints())
    {
        createSdrDragEntries_PointDrag();
    }
    else if(getSdrDragView().IsDraggingGluePoints())
    {
        createSdrDragEntries_GlueDrag();
    }
    else
    {
        if(getSolidDraggingActive())
        {
            createSdrDragEntries_SolidDrag();
        }
        else
        {
            createSdrDragEntries_PolygonDrag();
        }
    }
}
 
void SdrDragMethod::createSdrDragEntryForSdrObject(const SdrObject& rOriginal)
{
    // add full object drag; Clone() at the object has to work
    // for this
    addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntrySdrObject(rOriginal, true/*bModify*/)));
}
 
void SdrDragMethod::insertNewlyCreatedOverlayObjectForSdrDragMethod(
    std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject,
    const sdr::contact::ObjectContact& rObjectContact,
    sdr::overlay::OverlayManager& rOverlayManager)
{
    // check if we have an OverlayObject
    if(!pOverlayObject)
    {
        return;
    }
 
    // add to OverlayManager
    rOverlayManager.add(*pOverlayObject);
 
    // Add GridOffset for non-linear ViewToDevice transformation (calc)
    if(rObjectContact.supportsGridOffsets())
    {
        const basegfx::B2DRange& rNewRange(pOverlayObject->getBaseRange());
 
        if(!rNewRange.isEmpty())
        {
            basegfx::B2DVector aOffset(0.0, 0.0);
            rObjectContact.calculateGridOffsetForB2DRange(aOffset, rNewRange);
 
            if(!aOffset.equalZero())
            {
                pOverlayObject->setOffset(aOffset);
            }
        }
    }
 
    // add to local OverlayObjectList - ownership change (!)
    maOverlayObjectList.append(std::move(pOverlayObject));
}
 
void SdrDragMethod::createSdrDragEntries_SolidDrag()
{
    const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
    const size_t nMarkCount(rMarkList.GetMarkCount());
    SdrPageView* pPV = getSdrDragView().GetSdrPageView();
 
    if(!pPV)
        return;
 
    for(size_t a = 0; a < nMarkCount; ++a)
    {
        SdrMark* pM = rMarkList.GetMark(a);
 
        if(pM->GetPageView() == pPV)
        {
            const SdrObject* pObject = pM->GetMarkedSdrObj();
 
            if(pObject)
            {
                if(pPV->PageWindowCount())
                {
                    SdrObjListIter aIter(*pObject);
 
                    while(aIter.IsMore())
                    {
                        SdrObject* pCandidate = aIter.Next();
 
                        if(pCandidate)
                        {
                            const bool bSuppressFullDrag(!pCandidate->supportsFullDrag());
                            bool bAddWireframe(bSuppressFullDrag);
 
                            if(!bAddWireframe && !pCandidate->HasLineStyle())
                            {
                                // add wireframe for objects without outline
                                bAddWireframe = true;
                            }
 
                            if(!bSuppressFullDrag)
                            {
                                // add full object drag; Clone() at the object has to work
                                // for this
                                createSdrDragEntryForSdrObject(*pCandidate);
                            }
 
                            if(bAddWireframe)
                            {
                                // when dragging a 50% transparent copy of a filled or not filled object without
                                // outline, this is normally hard to see. Add extra wireframe in that case. This
                                // works nice e.g. with text frames etc.
                                addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(pCandidate->TakeXorPoly())));
                            }
                        }
                    }
                }
            }
        }
    }
}
 
void SdrDragMethod::createSdrDragEntries_PolygonDrag()
{
    const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
    const size_t nMarkCount(rMarkList.GetMarkCount());
    bool bNoPolygons(getSdrDragView().IsNoDragXorPolys() || nMarkCount > SdrDragView::GetDragXorPolyLimit());
    basegfx::B2DPolyPolygon aResult;
    sal_uInt32 nPointCount(0);
 
    for(size_t a = 0; !bNoPolygons && a < nMarkCount; ++a)
    {
        SdrMark* pM = rMarkList.GetMark(a);
 
        if(pM->GetPageView() == getSdrDragView().GetSdrPageView())
        {
            const basegfx::B2DPolyPolygon aNewPolyPolygon(pM->GetMarkedSdrObj()->TakeXorPoly());
 
            for(auto const& rPolygon : aNewPolyPolygon)
            {
                nPointCount += rPolygon.count();
            }
 
            if(nPointCount > SdrDragView::GetDragXorPointLimit())
            {
                bNoPolygons = true;
            }
 
            if(!bNoPolygons)
            {
                aResult.append(aNewPolyPolygon);
            }
        }
    }
 
    if(bNoPolygons)
    {
        const tools::Rectangle aR(getSdrDragView().GetSdrPageView()->MarkSnap());
        const basegfx::B2DRange aNewRectangle = vcl::unotools::b2DRectangleFromRectangle(aR);
        basegfx::B2DPolygon aNewPolygon(basegfx::utils::createPolygonFromRect(aNewRectangle));
 
        aResult = basegfx::B2DPolyPolygon(basegfx::utils::expandToCurve(aNewPolygon));
    }
 
    if(aResult.count())
    {
        addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(std::move(aResult))));
    }
}
 
void SdrDragMethod::createSdrDragEntries_PointDrag()
{
    const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
    const size_t nMarkCount(rMarkList.GetMarkCount());
    std::vector< basegfx::B2DPoint > aPositions;
 
    for(size_t nm = 0; nm < nMarkCount; ++nm)
    {
        SdrMark* pM = rMarkList.GetMark(nm);
 
        if(pM->GetPageView() == getSdrDragView().GetSdrPageView())
        {
            const SdrUShortCont& rPts = pM->GetMarkedPoints();
 
            if (!rPts.empty())
            {
                const SdrObject* pObj = pM->GetMarkedSdrObj();
                const SdrPathObj* pPath = dynamic_cast< const SdrPathObj* >(pObj);
 
                if(pPath)
                {
                    const basegfx::B2DPolyPolygon& aPathXPP = pPath->GetPathPoly();
 
                    if(aPathXPP.count())
                    {
                        for(const sal_uInt16 nObjPt : rPts)
                        {
                            sal_uInt32 nPolyNum, nPointNum;
 
                            if(sdr::PolyPolygonEditor::GetRelativePolyPoint(aPathXPP, nObjPt, nPolyNum, nPointNum))
                            {
                                aPositions.push_back(aPathXPP.getB2DPolygon(nPolyNum).getB2DPoint(nPointNum));
                            }
                        }
                    }
                }
            }
        }
    }
 
    if(!aPositions.empty())
    {
        addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPointGlueDrag(std::move(aPositions), true)));
    }
}
 
void SdrDragMethod::createSdrDragEntries_GlueDrag()
{
    const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
    const size_t nMarkCount(rMarkList.GetMarkCount());
    std::vector< basegfx::B2DPoint > aPositions;
 
    for(size_t nm = 0; nm < nMarkCount; ++nm)
    {
        SdrMark* pM = rMarkList.GetMark(nm);
 
        if(pM->GetPageView() == getSdrDragView().GetSdrPageView())
        {
            const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
 
            if (!rPts.empty())
            {
                const SdrObject* pObj = pM->GetMarkedSdrObj();
                const SdrGluePointList* pGPL = pObj->GetGluePointList();
 
                if (pGPL)
                {
                    for(const sal_uInt16 nObjPt : rPts)
                    {
                        const sal_uInt16 nGlueNum(pGPL->FindGluePoint(nObjPt));
 
                        if(SDRGLUEPOINT_NOTFOUND != nGlueNum)
                        {
                            const Point aPoint((*pGPL)[nGlueNum].GetAbsolutePos(*pObj));
                            aPositions.emplace_back(aPoint.X(), aPoint.Y());
                        }
                    }
                }
            }
        }
    }
 
    if(!aPositions.empty())
    {
        addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPointGlueDrag(std::move(aPositions), false)));
    }
}
 
OUString SdrDragMethod::ImpGetDescriptionStr(TranslateId pStrCacheID) const
{
    ImpGetDescriptionOptions nOpt=ImpGetDescriptionOptions::NONE;
    if (IsDraggingPoints()) {
        nOpt=ImpGetDescriptionOptions::POINTS;
    } else if (IsDraggingGluePoints()) {
        nOpt=ImpGetDescriptionOptions::GLUEPOINTS;
    }
    return getSdrDragView().ImpGetDescriptionString(pStrCacheID, nOpt);
}
 
SdrObject* SdrDragMethod::GetDragObj() const
{
    SdrObject* pObj=nullptr;
    if (getSdrDragView().mpDragHdl!=nullptr) pObj=getSdrDragView().mpDragHdl->GetObj();
    if (pObj==nullptr) pObj=getSdrDragView().mpMarkedObj;
    return pObj;
}
 
SdrPageView* SdrDragMethod::GetDragPV() const
{
    SdrPageView* pPV=nullptr;
    if (getSdrDragView().mpDragHdl!=nullptr) pPV=getSdrDragView().mpDragHdl->GetPageView();
    if (pPV==nullptr) pPV=getSdrDragView().mpMarkedPV;
    return pPV;
}
 
void SdrDragMethod::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
{
    // the original applies the transformation using TRGetBaseGeometry/TRSetBaseGeometry.
    // Later this should be the only needed one for linear transforms (not for SdrDragCrook and
    // SdrDragDistort, those are NOT linear). Currently, this can not yet be used since the
    // special handling of rotate/mirror due to the not-being-able to handle it in the old
    // drawinglayer stuff. Text would currently not correctly be mirrored in the preview.
    basegfx::B2DHomMatrix aObjectTransform;
    basegfx::B2DPolyPolygon aObjectPolyPolygon;
    bool bPolyUsed(rTarget.TRGetBaseGeometry(aObjectTransform, aObjectPolyPolygon));
 
    // apply transform to object transform
    aObjectTransform *= getCurrentTransformation();
 
    if(bPolyUsed)
    {
        // do something special since the object size is in the polygon
        // break up matrix to get the scale
        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(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(
            basegfx::utils::createTranslateB2DHomMatrix(
                -aPolyRange.getMinX(),
                -aPolyRange.getMinY()));
        aPolyTransform.scale(fScaleX, fScaleY);
 
        // transform the polygon
        aObjectPolyPolygon.transform(aPolyTransform);
    }
 
    rTarget.TRSetBaseGeometry(getCurrentTransformation() * aObjectTransform, aObjectPolyPolygon);
}
 
void SdrDragMethod::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget)
{
    // original uses CurrentTransformation
    rTarget.transform(getCurrentTransformation());
}
 
SdrDragMethod::SdrDragMethod(SdrDragView& rNewView)
:   mrSdrDragView(rNewView),
    mbMoveOnly(false),
    mbSolidDraggingActive(getSdrDragView().IsSolidDragging()),
    mbShiftPressed(false)
{
    if(mbSolidDraggingActive && Application::GetSettings().GetStyleSettings().GetHighContrastMode())
    {
        // fallback to wireframe when high contrast is used
        mbSolidDraggingActive = false;
    }
}
 
SdrDragMethod::~SdrDragMethod()
{
    clearSdrDragEntries();
}
 
void SdrDragMethod::Show(bool IsValidSize)
{
    getSdrDragView().ShowDragObj(IsValidSize);
}
 
void SdrDragMethod::Hide()
{
    getSdrDragView().HideDragObj();
}
 
basegfx::B2DHomMatrix SdrDragMethod::getCurrentTransformation() const
{
    return basegfx::B2DHomMatrix();
}
 
void SdrDragMethod::CancelSdrDrag()
{
    Hide();
}
 
typedef std::map< const SdrObject*, SdrObject* > SdrObjectAndCloneMap;
 
void SdrDragMethod::CreateOverlayGeometry(
    sdr::overlay::OverlayManager& rOverlayManager,
    const sdr::contact::ObjectContact& rObjectContact, bool bIsGeometrySizeValid)
{
    // We do client-side object manipulation with the Kit API
    if (comphelper::LibreOfficeKit::isActive())
        return;
 
    // create SdrDragEntries on demand
    if(maSdrDragEntries.empty())
    {
        createSdrDragEntries();
    }
 
    // if there are entries, derive OverlayObjects from the entries, including
    // modification from current interactive state
    if(!maSdrDragEntries.empty())
    {
        // #i54102# SdrDragEntrySdrObject creates clones of SdrObjects as base for creating the needed
        // primitives, holding the original and the clone. If connectors (Edges) are involved,
        // the cloned connectors need to be connected to the cloned SdrObjects (after cloning
        // they are connected to the original SdrObjects). To do so, trigger the preparation
        // steps for SdrDragEntrySdrObject, save an association of (orig, clone) in a helper
        // and evtl. remember if it was an edge
        SdrObjectAndCloneMap aOriginalAndClones;
        std::vector< SdrEdgeObj* > aEdges;
 
        // #i54102# execute prepareCurrentState for all SdrDragEntrySdrObject, register pair of original and
        // clone, remember edges
        for(auto const & a: maSdrDragEntries)
        {
            SdrDragEntrySdrObject* pSdrDragEntrySdrObject = dynamic_cast< SdrDragEntrySdrObject*>(a.get());
 
            if(pSdrDragEntrySdrObject)
            {
                pSdrDragEntrySdrObject->prepareCurrentState(*this);
 
                SdrEdgeObj* pSdrEdgeObj = dynamic_cast< SdrEdgeObj* >(pSdrDragEntrySdrObject->getClone());
 
                if(pSdrEdgeObj)
                {
                    aEdges.push_back(pSdrEdgeObj);
                }
 
                if(pSdrDragEntrySdrObject->getClone())
                {
                    aOriginalAndClones[&pSdrDragEntrySdrObject->getOriginal()] = pSdrDragEntrySdrObject->getClone();
                }
            }
        }
 
        // #i54102# if there are edges, reconnect their ends to the corresponding clones (if found)
        for(SdrEdgeObj* pSdrEdgeObj: aEdges)
        {
            SdrObject* pConnectedTo = pSdrEdgeObj->GetConnectedNode(true);
 
            if(pConnectedTo)
            {
                SdrObjectAndCloneMap::iterator aEntry = aOriginalAndClones.find(pConnectedTo);
 
                if(aEntry != aOriginalAndClones.end())
                {
                    pSdrEdgeObj->ConnectToNode(true, aEntry->second);
                }
            }
 
            pConnectedTo = pSdrEdgeObj->GetConnectedNode(false);
 
            if(pConnectedTo)
            {
                SdrObjectAndCloneMap::iterator aEntry = aOriginalAndClones.find(pConnectedTo);
 
                if(aEntry != aOriginalAndClones.end())
                {
                    pSdrEdgeObj->ConnectToNode(false, aEntry->second);
                }
            }
        }
 
        // collect primitives for visualisation
        drawinglayer::primitive2d::Primitive2DContainer aResult;
        drawinglayer::primitive2d::Primitive2DContainer aResultTransparent;
 
        for(auto & pCandidate: maSdrDragEntries)
        {
            const drawinglayer::primitive2d::Primitive2DContainer aCandidateResult(
                    pCandidate->createPrimitive2DSequenceInCurrentState(*this, bIsGeometrySizeValid));
 
            if(!aCandidateResult.empty())
            {
                if(pCandidate->getAddToTransparent())
                {
                    aResultTransparent.append(aCandidateResult);
                }
                else
                {
                    aResult.append(aCandidateResult);
                }
            }
        }
 
        if(DoAddConnectorOverlays())
        {
            drawinglayer::primitive2d::Primitive2DContainer aConnectorOverlays(AddConnectorOverlays());
 
            if(!aConnectorOverlays.empty())
            {
                // add connector overlays to transparent part
                aResultTransparent.append(std::move(aConnectorOverlays));
            }
        }
 
        if(!aResult.empty())
        {
            std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(
                new sdr::overlay::OverlayPrimitive2DSequenceObject(
                    std::move(aResult)));
 
            insertNewlyCreatedOverlayObjectForSdrDragMethod(
                std::move(pNewOverlayObject),
                rObjectContact,
                rOverlayManager);
        }
 
        if(!aResultTransparent.empty())
        {
            aResultTransparent = drawinglayer::primitive2d::Primitive2DContainer {
                new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aResultTransparent), 0.5)
            };
 
            std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(
                new sdr::overlay::OverlayPrimitive2DSequenceObject(
                    std::move(aResultTransparent)));
 
            insertNewlyCreatedOverlayObjectForSdrDragMethod(
                std::move(pNewOverlayObject),
                rObjectContact,
                rOverlayManager);
        }
    }
 
    // add DragStripes if necessary (help lines cross the page when dragging)
    if(!getSdrDragView().IsDragStripes())
        return;
 
    tools::Rectangle aActionRectangle;
    getSdrDragView().TakeActionRect(aActionRectangle);
 
    const basegfx::B2DPoint aTopLeft(aActionRectangle.Left(), aActionRectangle.Top());
    const basegfx::B2DPoint aBottomRight(aActionRectangle.Right(), aActionRectangle.Bottom());
    std::unique_ptr<sdr::overlay::OverlayRollingRectangleStriped> pNew(
        new sdr::overlay::OverlayRollingRectangleStriped(
            aTopLeft,
            aBottomRight,
            true,
            false));
 
    insertNewlyCreatedOverlayObjectForSdrDragMethod(
        std::move(pNew),
        rObjectContact,
        rOverlayManager);
}
 
void SdrDragMethod::destroyOverlayGeometry()
{
    maOverlayObjectList.clear();
}
 
bool SdrDragMethod::DoAddConnectorOverlays()
{
    // these conditions are translated from SdrDragView::ImpDrawEdgeXor
    const SdrMarkList& rMarkedNodes = getSdrDragView().GetEdgesOfMarkedNodes();
 
    if(!rMarkedNodes.GetMarkCount())
    {
        return false;
    }
 
    if(getSdrDragView().IsDraggingPoints() || getSdrDragView().IsDraggingGluePoints())
    {
        return false;
    }
 
    if(!getMoveOnly() && !(
        dynamic_cast<const SdrDragMove*>(this) != nullptr || dynamic_cast<const SdrDragResize*>(this) != nullptr ||
        dynamic_cast<const SdrDragRotate*>(this) != nullptr || dynamic_cast<const SdrDragMirror*>(this) != nullptr ))
    {
        return false;
    }
 
    // one more migrated from SdrEdgeObj::NspToggleEdgeXor
    if( dynamic_cast< const SdrDragObjOwn* >(this) != nullptr || dynamic_cast< const SdrDragMovHdl* >(this) != nullptr )
    {
        return false;
    }
 
    return true;
}
 
drawinglayer::primitive2d::Primitive2DContainer SdrDragMethod::AddConnectorOverlays()
{
    drawinglayer::primitive2d::Primitive2DContainer aRetval;
    const bool bDetail(getMoveOnly());
    const SdrMarkList& rMarkedNodes = getSdrDragView().GetEdgesOfMarkedNodes();
 
    for(size_t a = 0; a < rMarkedNodes.GetMarkCount(); ++a)
    {
        SdrMark* pEM = rMarkedNodes.GetMark(a);
 
        if(pEM && pEM->GetMarkedSdrObj())
        {
            SdrEdgeObj* pEdge = dynamic_cast< SdrEdgeObj* >(pEM->GetMarkedSdrObj());
 
            if(pEdge)
            {
                basegfx::B2DPolygon aEdgePolygon(pEdge->ImplAddConnectorOverlay(*this, pEM->IsCon1(), pEM->IsCon2(), bDetail));
 
                if(aEdgePolygon.count())
                {
                    // this polygon is a temporary calculated connector path, so it is not possible to fetch
                    // the needed primitives directly from the pEdge object which does not get changed. If full
                    // drag is on, use the SdrObjects ItemSet to create an adequate representation
                    bool bUseSolidDragging(getSolidDraggingActive());
 
                    if(bUseSolidDragging)
                    {
                        // switch off solid dragging if connector is not visible
                        if(!pEdge->HasLineStyle())
                        {
                            bUseSolidDragging = false;
                        }
                    }
 
                    if(bUseSolidDragging)
                    {
                        const SfxItemSet& rItemSet = pEdge->GetMergedItemSet();
                        const drawinglayer::attribute::SdrLineAttribute aLine(
                            drawinglayer::primitive2d::createNewSdrLineAttribute(rItemSet));
 
                        if(!aLine.isDefault())
                        {
                            const drawinglayer::attribute::SdrLineStartEndAttribute aLineStartEnd(
                                drawinglayer::primitive2d::createNewSdrLineStartEndAttribute(
                                    rItemSet,
                                    aLine.getWidth()));
 
                            aRetval.push_back(drawinglayer::primitive2d::createPolygonLinePrimitive(
                                    aEdgePolygon,
                                    aLine,
                                    aLineStartEnd));
                        }
                    }
                    else
                    {
                        basegfx::BColor aColA(SvtOptionsDrawinglayer::GetStripeColorA().getBColor());
                        basegfx::BColor aColB(SvtOptionsDrawinglayer::GetStripeColorB().getBColor());
                        const double fStripeLength(officecfg::Office::Common::Drawinglayer::StripeLength::get());
 
                        if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
                        {
                            aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor();
                            aColB.invert();
                        }
 
                        drawinglayer::primitive2d::Primitive2DReference aPolyPolygonMarkerPrimitive2D(
                            new drawinglayer::primitive2d::PolygonMarkerPrimitive2D(
                                std::move(aEdgePolygon), aColA, aColB, fStripeLength));
                        aRetval.push_back(aPolyPolygonMarkerPrimitive2D);
                    }
                }
            }
        }
    }
 
    return aRetval;
}
 
 
SdrDragMovHdl::SdrDragMovHdl(SdrDragView& rNewView)
:   SdrDragMethod(rNewView)
{
}
 
void SdrDragMovHdl::createSdrDragEntries()
{
    // SdrDragMovHdl does not use the default drags,
    // but creates nothing
}
 
OUString SdrDragMovHdl::GetSdrDragComment() const
{
    OUString aStr=SvxResId(STR_DragMethMovHdl);
    if (getSdrDragView().IsDragWithCopy()) aStr+=SvxResId(STR_EditWithCopy);
    return aStr;
}
 
bool SdrDragMovHdl::BeginSdrDrag()
{
    if( !GetDragHdl() )
        return false;
 
    DragStat().SetRef1(GetDragHdl()->GetPos());
    DragStat().SetShown(!DragStat().IsShown());
    SdrHdlKind eKind=GetDragHdl()->GetKind();
    SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1);
    SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2);
 
    if (eKind==SdrHdlKind::MirrorAxis)
    {
        if (pH1==nullptr || pH2==nullptr)
        {
            OSL_FAIL("SdrDragMovHdl::BeginSdrDrag(): Moving the axis of reflection: reference handles not found.");
            return false;
        }
 
        DragStat().SetActionRect(tools::Rectangle(pH1->GetPos(),pH2->GetPos()));
    }
    else
    {
        Point aPt(GetDragHdl()->GetPos());
        DragStat().SetActionRect(tools::Rectangle(aPt,aPt));
    }
 
    return true;
}
 
void SdrDragMovHdl::MoveSdrDrag(const Point& rNoSnapPnt)
{
    Point aPnt(rNoSnapPnt);
 
    if ( !(GetDragHdl() && DragStat().CheckMinMoved(rNoSnapPnt)))
        return;
 
    if (GetDragHdl()->GetKind()==SdrHdlKind::MirrorAxis)
    {
        SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1);
        SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2);
 
        if (pH1==nullptr || pH2==nullptr)
            return;
 
        if (!DragStat().IsNoSnap())
        {
            tools::Long nBestXSnap=0;
            tools::Long nBestYSnap=0;
            bool bXSnapped=false;
            bool bYSnapped=false;
            Point aDif(aPnt-DragStat().GetStart());
            getSdrDragView().CheckSnap(Ref1()+aDif,nBestXSnap,nBestYSnap,bXSnapped,bYSnapped);
            getSdrDragView().CheckSnap(Ref2()+aDif,nBestXSnap,nBestYSnap,bXSnapped,bYSnapped);
            aPnt.AdjustX(nBestXSnap );
            aPnt.AdjustY(nBestYSnap );
        }
 
        if (aPnt!=DragStat().GetNow())
        {
            Hide();
            DragStat().NextMove(aPnt);
            Point aDif(DragStat().GetNow()-DragStat().GetStart());
            pH1->SetPos(Ref1()+aDif);
            pH2->SetPos(Ref2()+aDif);
 
            SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis);
 
            if(pHM)
                pHM->Touch();
 
            Show();
            DragStat().SetActionRect(tools::Rectangle(pH1->GetPos(),pH2->GetPos()));
        }
    }
    else
    {
        if (!DragStat().IsNoSnap()) SnapPos(aPnt);
        Degree100 nSA(0);
 
        if (getSdrDragView().IsAngleSnapEnabled())
            nSA=getSdrDragView().GetSnapAngle();
 
        if (getSdrDragView().IsMirrorAllowed(true,true))
        { // limited
            if (!getSdrDragView().IsMirrorAllowed()) nSA=4500_deg100;
            if (!getSdrDragView().IsMirrorAllowed(true)) nSA=9000_deg100;
        }
 
        if (getSdrDragView().IsOrtho() && nSA!=9000_deg100)
            nSA=4500_deg100;
 
        if (nSA)
        { // angle snapping
            SdrHdlKind eRef=SdrHdlKind::Ref1;
 
            if (GetDragHdl()->GetKind()==SdrHdlKind::Ref1)
                eRef=SdrHdlKind::Ref2;
 
            SdrHdl* pH=GetHdlList().GetHdl(eRef);
 
            if (pH!=nullptr)
            {
                Point aRef(pH->GetPos());
                Degree100 nAngle=NormAngle36000(GetAngle(aPnt-aRef));
                Degree100 nNewAngle=nAngle;
                nNewAngle+=nSA/2_deg100;
                nNewAngle/=nSA;
                nNewAngle*=nSA;
                nNewAngle=NormAngle36000(nNewAngle);
                double a=toRadians(nNewAngle-nAngle);
                double nSin=sin(a);
                double nCos=cos(a);
                RotatePoint(aPnt,aRef,nSin,nCos);
 
                // eliminate rounding errors for certain values
                if (nSA==9000_deg100)
                {
                    if (nNewAngle==0_deg100    || nNewAngle==18000_deg100) aPnt.setY(aRef.Y() );
                    if (nNewAngle==9000_deg100 || nNewAngle==27000_deg100) aPnt.setX(aRef.X() );
                }
 
                if (nSA==4500_deg100)
                    OrthoDistance8(aRef,aPnt,true);
            }
        }
 
        if (aPnt!=DragStat().GetNow())
        {
            Hide();
            DragStat().NextMove(aPnt);
            GetDragHdl()->SetPos(DragStat().GetNow());
            SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis);
 
            if(pHM)
                pHM->Touch();
 
            Show();
            DragStat().SetActionRect(tools::Rectangle(aPnt,aPnt));
        }
    }
}
 
bool SdrDragMovHdl::EndSdrDrag(bool /*bCopy*/)
{
    if( GetDragHdl() )
    {
        switch (GetDragHdl()->GetKind())
        {
            case SdrHdlKind::Ref1:
                Ref1()=DragStat().GetNow();
                break;
 
            case SdrHdlKind::Ref2:
                Ref2()=DragStat().GetNow();
                break;
 
            case SdrHdlKind::MirrorAxis:
                Ref1()+=DragStat().GetNow()-DragStat().GetStart();
                Ref2()+=DragStat().GetNow()-DragStat().GetStart();
                break;
 
            default: break;
        }
    }
 
    return true;
}
 
void SdrDragMovHdl::CancelSdrDrag()
{
    Hide();
 
    SdrHdl* pHdl = GetDragHdl();
    if( pHdl )
        pHdl->SetPos(DragStat().GetRef1());
 
    SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis);
 
    if(pHM)
        pHM->Touch();
}
 
PointerStyle SdrDragMovHdl::GetSdrDragPointer() const
{
    const SdrHdl* pHdl = GetDragHdl();
 
    if (pHdl!=nullptr)
    {
        return pHdl->GetPointer();
    }
 
    return PointerStyle::RefHand;
}
 
 
SdrDragObjOwn::SdrDragObjOwn(SdrDragView& rNewView)
:   SdrDragMethod(rNewView)
{
    const SdrObject* pObj = GetDragObj();
 
    if(pObj)
    {
        // suppress full drag for some object types
        setSolidDraggingActive(pObj->supportsFullDrag());
    }
}
 
SdrDragObjOwn::~SdrDragObjOwn()
{
}
 
void SdrDragObjOwn::createSdrDragEntries()
{
    if(!mxClone)
        return;
 
    basegfx::B2DPolyPolygon aDragPolyPolygon;
    bool bAddWireframe(true);
 
    if(getSolidDraggingActive())
    {
        SdrPageView* pPV = getSdrDragView().GetSdrPageView();
 
        if(pPV && pPV->PageWindowCount())
        {
            addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntrySdrObject(*mxClone, false)));
 
            // potentially no wireframe needed, full drag works
            bAddWireframe = false;
        }
    }
 
    if(!bAddWireframe)
    {
        // check for extra conditions for wireframe, e.g. no border at
        // objects
        if(!mxClone->HasLineStyle())
        {
            bAddWireframe = true;
        }
    }
 
    if(bAddWireframe)
    {
        // use wireframe poly when full drag is off or did not work
        aDragPolyPolygon = mxClone->TakeXorPoly();
    }
 
    // add evtl. extra DragPolyPolygon
    const basegfx::B2DPolyPolygon aSpecialDragPolyPolygon(mxClone->getSpecialDragPoly(DragStat()));
 
    if(aSpecialDragPolyPolygon.count())
    {
        aDragPolyPolygon.append(aSpecialDragPolyPolygon);
    }
 
    if(aDragPolyPolygon.count())
    {
        addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(std::move(aDragPolyPolygon))));
    }
}
 
OUString SdrDragObjOwn::GetSdrDragComment() const
{
    OUString aStr;
    // #i103058# get info string from the clone preferred, the original will
    // not be changed. For security, use original as fallback
    if(mxClone)
    {
        aStr = mxClone->getSpecialDragComment(DragStat());
    }
    else
    {
        const SdrObject* pObj = GetDragObj();
 
        if(pObj)
        {
            aStr = pObj->getSpecialDragComment(DragStat());
        }
    }
    return aStr;
}
 
bool SdrDragObjOwn::BeginSdrDrag()
{
    if(!mxClone)
    {
        const SdrObject* pObj = GetDragObj();
 
        if(pObj && !pObj->IsResizeProtect())
        {
            if(pObj->beginSpecialDrag(DragStat()))
            {
                // create initial clone to have a start visualization
                mxClone = pObj->getFullDragClone();
                mxClone->applySpecialDrag(DragStat());
 
                return true;
            }
        }
    }
 
    return false;
}
 
void SdrDragObjOwn::MoveSdrDrag(const Point& rNoSnapPnt)
{
    const SdrObject* pObj = GetDragObj();
 
    if (!pObj)
        // No object to drag.  Bail out.
        return;
 
    Point aPnt(rNoSnapPnt);
    SdrPageView* pPV = GetDragPV();
 
    if (!pPV)
        // No page view available.  Bail out.
        return;
 
    if(!DragStat().IsNoSnap())
    {
        SnapPos(aPnt);
    }
    if(getSdrDragView().IsOrtho())
    {
        if (DragStat().IsOrtho8Possible())
        {
            OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
        }
        else if (DragStat().IsOrtho4Possible())
        {
            OrthoDistance4(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
        }
    }
 
    if (!DragStat().CheckMinMoved(rNoSnapPnt))
        // Not moved by the minimum threshold. Nothing to do.
        return;
 
    Hide();
    DragStat().NextMove(aPnt);
 
    // since SdrDragObjOwn currently supports no transformation of
    // existing SdrDragEntries but only their recreation, a recreation
    // after every move is needed in this mode. Delete existing
    // SdrDragEntries here  to force their recreation in the following Show().
    clearSdrDragEntries();
 
    // delete current clone (after the last reference to it is deleted above)
    mxClone.clear();
 
    // create a new clone and modify to current drag state
    mxClone = pObj->getFullDragClone();
    mxClone->applySpecialDrag(DragStat());
 
    // AutoGrowWidth may change for SdrTextObj due to the automatism used
    // with bDisableAutoWidthOnDragging, so not only geometry changes but
    // also this (pretty indirect) property change is possible. If it gets
    // changed, it needs to be copied to the original since nothing will
    // happen when it only changes in the drag clone
    const bool bOldAutoGrowWidth(pObj->GetMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue());
    const bool bNewAutoGrowWidth(mxClone->GetMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue());
 
    if (bOldAutoGrowWidth != bNewAutoGrowWidth)
    {
        GetDragObj()->SetMergedItem(makeSdrTextAutoGrowWidthItem(bNewAutoGrowWidth));
    }
 
    Show();
}
 
bool SdrDragObjOwn::EndSdrDrag(bool /*bCopy*/)
{
    Hide();
    std::vector< std::unique_ptr<SdrUndoAction> > vConnectorUndoActions;
    bool bRet = false;
    SdrObject* pObj = GetDragObj();
 
    if(pObj)
    {
        std::unique_ptr<SdrUndoAction> pUndo;
        std::unique_ptr<SdrUndoAction> pUndo2;
        const bool bUndo = getSdrDragView().IsUndoEnabled();
 
        if( bUndo )
        {
            getSdrDragView().EndTextEditCurrentView();
            if(!getSdrDragView().IsInsObjPoint() && pObj->IsInserted() )
            {
                if (DragStat().IsEndDragChangesAttributes())
                {
                    pUndo=getSdrDragView().GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pObj);
 
                    if (DragStat().IsEndDragChangesGeoAndAttributes())
                    {
                        vConnectorUndoActions = getSdrDragView().CreateConnectorUndo( *pObj );
                        pUndo2 = getSdrDragView().GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj);
                    }
                }
                else
                {
                    vConnectorUndoActions = getSdrDragView().CreateConnectorUndo( *pObj );
                    pUndo= getSdrDragView().GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj);
                }
            }
 
            if( pUndo )
            {
                getSdrDragView().BegUndo( pUndo->GetComment() );
            }
            else
            {
                getSdrDragView().BegUndo();
            }
        }
 
        // Maybe use operator = for setting changed object data (do not change selection in
        // view, this will destroy the interactor). This is possible since a clone is now
        // directly modified by the modifiers. Only SdrTableObj is adding own UNDOs
        // in its SdrTableObj::endSpecialDrag, so currently not possible. OTOH it uses
        // a CreateUndoGeoObject(), so maybe setting SetEndDragChangesAttributes is okay. I
        // will test this now
        tools::Rectangle aBoundRect0;
 
        if(pObj->GetUserCall())
        {
            aBoundRect0 = pObj->GetLastBoundRect();
        }
 
        bRet = pObj->applySpecialDrag(DragStat());
        if (DragStat().IsEndDragChangesLayout())
        {
            auto pGeoUndo = dynamic_cast<SdrUndoGeoObj*>(pUndo.get());
            if (pGeoUndo)
                pGeoUndo->SetSkipChangeLayout(true);
        }
 
        if(bRet)
        {
            pObj->SetChanged();
            pObj->BroadcastObjectChange();
            pObj->SendUserCall( SdrUserCallType::Resize, aBoundRect0 );
        }
 
        if(bRet && bUndo )
        {
            getSdrDragView().AddUndoActions( std::move(vConnectorUndoActions) );
 
            if ( pUndo )
            {
                getSdrDragView().AddUndo(std::move(pUndo));
            }
 
            if ( pUndo2 )
            {
                getSdrDragView().AddUndo(std::move(pUndo2));
            }
        }
 
        if( bUndo )
            getSdrDragView().EndUndo();
    }
 
    return bRet;
}
 
PointerStyle SdrDragObjOwn::GetSdrDragPointer() const
{
    const SdrHdl* pHdl=GetDragHdl();
 
    if (pHdl)
    {
        return pHdl->GetPointer();
    }
 
    return PointerStyle::Move;
}
 
 
void SdrDragMove::createSdrDragEntryForSdrObject(const SdrObject& rOriginal)
{
    // use the view-independent primitive representation (without
    // evtl. GridOffset, that may be applied to the DragEntry individually)
    drawinglayer::primitive2d::Primitive2DContainer xRetval;
    rOriginal.GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
    addSdrDragEntry(
        std::unique_ptr<SdrDragEntry>(
            new SdrDragEntryPrimitive2DSequence(
                std::move(xRetval))));
 
}
 
void SdrDragMove::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
{
    rTarget.Move(Size(DragStat().GetDX(), DragStat().GetDY()));
}
 
SdrDragMove::SdrDragMove(SdrDragView& rNewView)
    : SdrDragMethod(rNewView)
    , nBestXSnap(0)
    , nBestYSnap(0)
    , bXSnapped(false)
    , bYSnapped(false)
{
    setMoveOnly(true);
}
 
OUString SdrDragMove::GetSdrDragComment() const
{
    OUString aStr = ImpGetDescriptionStr(STR_DragMethMove)
            + " (x="
            + getSdrDragView().GetModel().GetMetricString(DragStat().GetDX())
            + " y="
            + getSdrDragView().GetModel().GetMetricString(DragStat().GetDY())
            + ")";
 
    if(getSdrDragView().IsDragWithCopy())
    {
        if(!getSdrDragView().IsInsObjPoint() && !getSdrDragView().IsInsGluePoint())
        {
            aStr += SvxResId(STR_EditWithCopy);
        }
    }
    return aStr;
}
 
bool SdrDragMove::BeginSdrDrag()
{
    DragStat().SetActionRect(GetMarkedRect());
    Show();
 
    return true;
}
 
basegfx::B2DHomMatrix SdrDragMove::getCurrentTransformation() const
{
    return basegfx::utils::createTranslateB2DHomMatrix(DragStat().GetDX(), DragStat().GetDY());
}
 
void SdrDragMove::ImpCheckSnap(const Point& rPt)
{
    Point aPt(rPt);
    SdrSnap nRet=SnapPos(aPt);
    aPt-=rPt;
 
    if (nRet & SdrSnap::XSNAPPED)
    {
        if (bXSnapped)
        {
            if (std::abs(aPt.X())<std::abs(nBestXSnap))
            {
                nBestXSnap=aPt.X();
            }
        }
        else
        {
            nBestXSnap=aPt.X();
            bXSnapped=true;
        }
    }
 
    if (!(nRet & SdrSnap::YSNAPPED))
        return;
 
    if (bYSnapped)
    {
        if (std::abs(aPt.Y())<std::abs(nBestYSnap))
        {
            nBestYSnap=aPt.Y();
        }
    }
    else
    {
        nBestYSnap=aPt.Y();
        bYSnapped=true;
    }
}
 
void SdrDragMove::MoveSdrDrag(const Point& rNoSnapPnt_)
{
    nBestXSnap=0;
    nBestYSnap=0;
    bXSnapped=false;
    bYSnapped=false;
    Point aNoSnapPnt(rNoSnapPnt_);
    const tools::Rectangle& aSR=GetMarkedRect();
    tools::Long nMovedx=aNoSnapPnt.X()-DragStat().GetStart().X();
    tools::Long nMovedy=aNoSnapPnt.Y()-DragStat().GetStart().Y();
    Point aLO(aSR.TopLeft());      aLO.AdjustX(nMovedx ); aLO.AdjustY(nMovedy );
    Point aRU(aSR.BottomRight());  aRU.AdjustX(nMovedx ); aRU.AdjustY(nMovedy );
    Point aLU(aLO.X(),aRU.Y());
    Point aRO(aRU.X(),aLO.Y());
    ImpCheckSnap(aLO);
 
    if (!getSdrDragView().IsMoveSnapOnlyTopLeft())
    {
        ImpCheckSnap(aRO);
        ImpCheckSnap(aLU);
        ImpCheckSnap(aRU);
    }
 
    Point aPnt(aNoSnapPnt.X()+nBestXSnap,aNoSnapPnt.Y()+nBestYSnap);
    bool bOrtho=getSdrDragView().IsOrtho();
 
    if (bOrtho)
        OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
 
    if (!DragStat().CheckMinMoved(aNoSnapPnt))
        return;
 
    Point aPt1(aPnt);
    tools::Rectangle aLR(getSdrDragView().GetWorkArea());
    bool bWorkArea=!aLR.IsEmpty();
    bool bDragLimit=IsDragLimit();
 
    if (bDragLimit || bWorkArea)
    {
        tools::Rectangle aSR2(GetMarkedRect());
        Point aD(aPt1-DragStat().GetStart());
 
        if (bDragLimit)
        {
            tools::Rectangle aR2(GetDragLimitRect());
 
            if (bWorkArea)
                aLR.Intersection(aR2);
            else
                aLR=aR2;
        }
 
        if (aSR2.Left()>aLR.Left() || aSR2.Right()<aLR.Right())
        { // any space to move to?
            aSR2.Move(aD.X(),0);
 
            if (aSR2.Left()<aLR.Left())
            {
                aPt1.AdjustX( -(aSR2.Left()-aLR.Left()) );
            }
            else if (aSR2.Right()>aLR.Right())
            {
                aPt1.AdjustX( -(aSR2.Right()-aLR.Right()) );
            }
        }
        else
            aPt1.setX(DragStat().GetStart().X() ); // no space to move to
 
        if (aSR2.Top()>aLR.Top() || aSR2.Bottom()<aLR.Bottom())
        { // any space to move to?
            aSR2.Move(0,aD.Y());
 
            if (aSR2.Top()<aLR.Top())
            {
                aPt1.AdjustY( -(aSR2.Top()-aLR.Top()) );
            }
            else if (aSR2.Bottom()>aLR.Bottom())
            {
                aPt1.AdjustY( -(aSR2.Bottom()-aLR.Bottom()) );
            }
        }
        else
            aPt1.setY(DragStat().GetStart().Y() ); // no space to move to
    }
 
    if (getSdrDragView().IsDraggingGluePoints())
    { // restrict gluepoints to the BoundRect of the Obj
        aPt1-=DragStat().GetStart();
        const SdrMarkList& rMarkList = GetMarkedObjectList();
        const size_t nMarkCount = rMarkList.GetMarkCount();
 
        for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
        {
            const SdrMark* pM = rMarkList.GetMark(nMarkNum);
            const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
 
            if (!rPts.empty())
            {
                const SdrObject* pObj=pM->GetMarkedSdrObj();
                const SdrGluePointList* pGPL=pObj->GetGluePointList();
                tools::Rectangle aBound(pObj->GetCurrentBoundRect());
 
                for (sal_uInt16 nId : rPts)
                {
                    sal_uInt16 nGlueNum=pGPL->FindGluePoint(nId);
 
                    if (nGlueNum!=SDRGLUEPOINT_NOTFOUND)
                    {
                        Point aPt((*pGPL)[nGlueNum].GetAbsolutePos(*pObj));
                        aPt+=aPt1; // move by this much
                        if (aPt.X()<aBound.Left()  ) aPt1.AdjustX( -(aPt.X()-aBound.Left()) )  ;
                        if (aPt.X()>aBound.Right() ) aPt1.AdjustX( -(aPt.X()-aBound.Right()) ) ;
                        if (aPt.Y()<aBound.Top()   ) aPt1.AdjustY( -(aPt.Y()-aBound.Top()) )   ;
                        if (aPt.Y()>aBound.Bottom()) aPt1.AdjustY( -(aPt.Y()-aBound.Bottom()) );
                    }
                }
            }
        }
 
        aPt1+=DragStat().GetStart();
    }
 
    if (bOrtho)
        OrthoDistance8(DragStat().GetStart(),aPt1,false);
 
    if (aPt1!=DragStat().GetNow())
    {
        Hide();
        DragStat().NextMove(aPt1);
        tools::Rectangle aAction(GetMarkedRect());
        aAction.Move(DragStat().GetDX(),DragStat().GetDY());
        DragStat().SetActionRect(aAction);
        Show();
    }
}
 
bool SdrDragMove::EndSdrDrag(bool bCopy)
{
    Hide();
 
    if (getSdrDragView().IsInsObjPoint() || getSdrDragView().IsInsGluePoint())
        bCopy=false;
 
    if (IsDraggingPoints())
    {
        getSdrDragView().MoveMarkedPoints(Size(DragStat().GetDX(),DragStat().GetDY()));
    }
    else if (IsDraggingGluePoints())
    {
        getSdrDragView().MoveMarkedGluePoints(Size(DragStat().GetDX(),DragStat().GetDY()),bCopy);
    }
    else
    {
        getSdrDragView().MoveMarkedObj(Size(DragStat().GetDX(),DragStat().GetDY()),bCopy);
    }
 
    return true;
}
 
PointerStyle SdrDragMove::GetSdrDragPointer() const
{
    if (IsDraggingPoints() || IsDraggingGluePoints())
    {
        return PointerStyle::MovePoint;
    }
    else
    {
        return PointerStyle::Move;
    }
}
 
 
SdrDragResize::SdrDragResize(SdrDragView& rNewView)
:   SdrDragMethod(rNewView),
    aXFact(1,1),
    aYFact(1,1)
{
}
 
OUString SdrDragResize::GetSdrDragComment() const
{
    OUString aStr = ImpGetDescriptionStr(STR_DragMethResize);
    Fraction aFact1(1,1);
    Point aStart(DragStat().GetStart());
    Point aRef(DragStat().GetRef1());
    sal_Int32 nXDiv(aStart.X() - aRef.X());
 
    if(!nXDiv)
        nXDiv = 1;
 
    sal_Int32 nYDiv(aStart.Y() - aRef.Y());
 
    if(!nYDiv)
        nYDiv = 1;
 
    bool bX(aXFact != aFact1 && std::abs(nXDiv) > 1);
    bool bY(aYFact != aFact1 && std::abs(nYDiv) > 1);
 
    if(bX || bY)
    {
        aStr += " (";
 
        bool bEqual(aXFact == aYFact);
        if(bX)
        {
            if(!bEqual)
                aStr += "x=";
 
            aStr += SdrModel::GetPercentString(aXFact);
        }
 
        if(bY && !bEqual)
        {
            if(bX)
                aStr += " ";
 
            aStr += "y=" + SdrModel::GetPercentString(aYFact);
        }
 
        aStr += ")";
    }
 
    if(getSdrDragView().IsDragWithCopy())
        aStr += SvxResId(STR_EditWithCopy);
    return aStr;
}
 
bool SdrDragResize::BeginSdrDrag()
{
    SdrHdlKind eRefHdl=SdrHdlKind::Move;
    SdrHdl* pRefHdl=nullptr;
 
    switch (GetDragHdlKind())
    {
        case SdrHdlKind::UpperLeft: eRefHdl=SdrHdlKind::LowerRight; break;
        case SdrHdlKind::Upper: eRefHdl=SdrHdlKind::Lower; DragStat().SetHorFixed(true); break;
        case SdrHdlKind::UpperRight: eRefHdl=SdrHdlKind::LowerLeft; break;
        case SdrHdlKind::Left : eRefHdl=SdrHdlKind::Right; DragStat().SetVerFixed(true); break;
        case SdrHdlKind::Right: eRefHdl=SdrHdlKind::Left ; DragStat().SetVerFixed(true); break;
        case SdrHdlKind::LowerLeft: eRefHdl=SdrHdlKind::UpperRight; break;
        case SdrHdlKind::Lower: eRefHdl=SdrHdlKind::Upper; DragStat().SetHorFixed(true); break;
        case SdrHdlKind::LowerRight: eRefHdl=SdrHdlKind::UpperLeft; break;
        default: break;
    }
 
    if (eRefHdl!=SdrHdlKind::Move)
        pRefHdl=GetHdlList().GetHdl(eRefHdl);
 
    if (pRefHdl!=nullptr && !getSdrDragView().IsResizeAtCenter())
    {
        DragStat().SetRef1(pRefHdl->GetPos());
    }
    else
    {
        SdrHdl* pRef1=GetHdlList().GetHdl(SdrHdlKind::UpperLeft);
        SdrHdl* pRef2=GetHdlList().GetHdl(SdrHdlKind::LowerRight);
 
        if (pRef1!=nullptr && pRef2!=nullptr)
        {
            DragStat().SetRef1(tools::Rectangle(pRef1->GetPos(),pRef2->GetPos()).Center());
        }
        else
        {
            DragStat().SetRef1(GetMarkedRect().Center());
        }
    }
 
    Show();
 
    return true;
}
 
basegfx::B2DHomMatrix SdrDragResize::getCurrentTransformation() const
{
    basegfx::B2DHomMatrix aRetval(basegfx::utils::createTranslateB2DHomMatrix(
        -DragStat().GetRef1().X(), -DragStat().GetRef1().Y()));
    aRetval.scale(double(aXFact), double(aYFact));
    aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y());
 
    return aRetval;
}
 
void SdrDragResize::MoveSdrDrag(const Point& rNoSnapPnt)
{
    Point aPnt(GetSnapPos(rNoSnapPnt));
    Point aStart(DragStat().GetStart());
    Point aRef(DragStat().GetRef1());
    Fraction aMaxFact(0x7FFFFFFF,1);
    tools::Rectangle aLR(getSdrDragView().GetWorkArea());
    bool bWorkArea=!aLR.IsEmpty();
    bool bDragLimit=IsDragLimit();
 
    if (bDragLimit || bWorkArea)
    {
        tools::Rectangle aSR(GetMarkedRect());
 
        if (bDragLimit)
        {
            tools::Rectangle aR2(GetDragLimitRect());
 
            if (bWorkArea)
                aLR.Intersection(aR2);
            else
                aLR=aR2;
        }
 
        if (aPnt.X()<aLR.Left())
            aPnt.setX(aLR.Left() );
        else if (aPnt.X()>aLR.Right())
            aPnt.setX(aLR.Right() );
 
        if (aPnt.Y()<aLR.Top())
            aPnt.setY(aLR.Top() );
        else if (aPnt.Y()>aLR.Bottom())
            aPnt.setY(aLR.Bottom() );
 
        if (aRef.X()>aSR.Left())
        {
            Fraction aMax(aRef.X()-aLR.Left(),aRef.X()-aSR.Left());
 
            if (aMax<aMaxFact)
                aMaxFact=aMax;
        }
 
        if (aRef.X()<aSR.Right())
        {
            Fraction aMax(aLR.Right()-aRef.X(),aSR.Right()-aRef.X());
 
            if (aMax<aMaxFact)
                aMaxFact=aMax;
        }
 
        if (aRef.Y()>aSR.Top())
        {
            Fraction aMax(aRef.Y()-aLR.Top(),aRef.Y()-aSR.Top());
 
            if (aMax<aMaxFact)
                aMaxFact=aMax;
        }
 
        if (aRef.Y()<aSR.Bottom())
        {
            Fraction aMax(aLR.Bottom()-aRef.Y(),aSR.Bottom()-aRef.Y());
 
            if (aMax<aMaxFact)
                aMaxFact=aMax;
        }
    }
 
    tools::Long nXDiv=aStart.X()-aRef.X(); if (nXDiv==0) nXDiv=1;
    tools::Long nYDiv=aStart.Y()-aRef.Y(); if (nYDiv==0) nYDiv=1;
    tools::Long nXMul=aPnt.X()-aRef.X();
    tools::Long nYMul=aPnt.Y()-aRef.Y();
 
    if (nXDiv<0)
    {
        nXDiv=-nXDiv;
        nXMul=-nXMul;
    }
 
    if (nYDiv<0)
    {
        nYDiv=-nYDiv;
        nYMul=-nYMul;
    }
 
    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;
            }
 
            if (DragStat().IsVerFixed())
            {
                bYNeg=false;
                nYMul=1;
                nYDiv=1;
            }
        }
    }
 
    Fraction aNewXFact(nXMul,nXDiv);
    Fraction aNewYFact(nYMul,nYDiv);
 
    if (bOrtho)
    {
        if (aNewXFact>aMaxFact)
        {
            aNewXFact=aMaxFact;
            aNewYFact=aMaxFact;
        }
 
        if (aNewYFact>aMaxFact)
        {
            aNewXFact=aMaxFact;
            aNewYFact=aMaxFact;
        }
    }
 
    if (bXNeg)
        aNewXFact=Fraction(-aNewXFact.GetNumerator(),aNewXFact.GetDenominator());
 
    if (bYNeg)
        aNewYFact=Fraction(-aNewYFact.GetNumerator(),aNewYFact.GetDenominator());
 
    if (DragStat().CheckMinMoved(aPnt))
    {
        if ((!DragStat().IsHorFixed() && aPnt.X()!=DragStat().GetNow().X()) ||
            (!DragStat().IsVerFixed() && aPnt.Y()!=DragStat().GetNow().Y()))
        {
            Hide();
            DragStat().NextMove(aPnt);
            aXFact=aNewXFact;
            aYFact=aNewYFact;
 
            aNewXFact = double(aNewXFact) > 0 ? aNewXFact : Fraction(1, 1);
            aNewYFact = double(aNewYFact) > 0 ? aNewYFact : Fraction(1, 1);
            Size aTargetSize(
                    GetMarkedRect().GetSize().scale(aNewXFact.GetNumerator(), aNewXFact.GetDenominator(),
                                                    aNewYFact.GetNumerator(), aNewYFact.GetDenominator()));
            Show(getSdrDragView().IsMarkedObjSizeValid(aTargetSize));
        }
    }
}
 
void SdrDragResize::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
{
    rTarget.Resize(DragStat().GetRef1(),aXFact,aYFact);
}
 
bool SdrDragResize::EndSdrDrag(bool bCopy)
{
    Hide();
 
    if (IsDraggingPoints())
    {
        getSdrDragView().ResizeMarkedPoints(DragStat().GetRef1(),aXFact,aYFact);
    }
    else if (IsDraggingGluePoints())
    {
        getSdrDragView().ResizeMarkedGluePoints(DragStat().GetRef1(),aXFact,aYFact,bCopy);
    }
    else
    {
        getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),aXFact,aYFact,bCopy);
    }
 
    return true;
}
 
PointerStyle SdrDragResize::GetSdrDragPointer() const
{
    const SdrHdl* pHdl=GetDragHdl();
 
    if (pHdl!=nullptr)
    {
        return pHdl->GetPointer();
    }
 
    return PointerStyle::Move;
}
 
 
void SdrDragRotate::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
{
    rTarget.Rotate(DragStat().GetRef1(), nAngle, nSin, nCos);
}
 
SdrDragRotate::SdrDragRotate(SdrDragView& rNewView)
:   SdrDragMethod(rNewView),
    nSin(0.0),
    nCos(1.0),
    nAngle0(0),
    nAngle(0),
    bRight(false)
{
}
 
OUString SdrDragRotate::GetSdrDragComment() const
{
    OUString aStr = ImpGetDescriptionStr(STR_DragMethRotate) +
        " (";
    Degree100 nTmpAngle(NormAngle36000(nAngle));
 
    if(bRight && nAngle)
    {
        nTmpAngle -= 36000_deg100;
    }
 
    aStr += SdrModel::GetAngleString(nTmpAngle) + ")";
 
    if(getSdrDragView().IsDragWithCopy())
        aStr += SvxResId(STR_EditWithCopy);
    return aStr;
}
 
bool SdrDragRotate::BeginSdrDrag()
{
    SdrHdl* pH=GetHdlList().GetHdl(SdrHdlKind::Ref1);
 
    if (nullptr != pH)
    {
        Show();
        DragStat().SetRef1(pH->GetPos());
        nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1());
        return true;
    }
 
    // RotGrfFlyFrame: Support rotation around center *without* Ref1 (normally
    // the rotation point)
    const tools::Rectangle aLocalMarkRect(getSdrDragView().GetMarkedObjRect());
 
    if(!aLocalMarkRect.IsEmpty())
    {
        Show();
        DragStat().SetRef1(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() const
{
    return basegfx::utils::createRotateAroundPoint(
        DragStat().GetRef1().X(), DragStat().GetRef1().Y(),
        -atan2(nSin, nCos));
}
 
void SdrDragRotate::MoveSdrDrag(const Point& rPnt_)
{
    Point aPnt(rPnt_);
    if (!DragStat().CheckMinMoved(aPnt))
        return;
 
    Degree100 nNewAngle=NormAngle36000(GetAngle(aPnt-DragStat().GetRef1())-nAngle0);
    Degree100 nSA(0);
 
    if (getSdrDragView().IsAngleSnapEnabled())
        nSA=getSdrDragView().GetSnapAngle();
 
    if (!getSdrDragView().IsRotateAllowed())
        nSA=9000_deg100;
 
    if (nSA)
    { // angle snapping
        nNewAngle += nSA / 2_deg100;
        nNewAngle /= nSA;
        nNewAngle *= nSA;
    }
 
    nNewAngle=NormAngle18000(nNewAngle);
 
    if (nAngle==nNewAngle)
        return;
 
    sal_uInt16 nSekt0=GetAngleSector(nAngle);
    sal_uInt16 nSekt1=GetAngleSector(nNewAngle);
 
    if (nSekt0==0 && nSekt1==3)
        bRight=true;
 
    if (nSekt0==3 && nSekt1==0)
        bRight=false;
 
    nAngle=nNewAngle;
    double a = toRadians(nAngle);
    double nSin1=sin(a); // calculate now, so as little time as possible
    double nCos1=cos(a); // passes between Hide() and Show()
    Hide();
    nSin=nSin1;
    nCos=nCos1;
    DragStat().NextMove(aPnt);
    Show();
}
 
bool SdrDragRotate::EndSdrDrag(bool bCopy)
{
    Hide();
 
    if (nAngle!=0_deg100)
    {
        if (IsDraggingPoints())
        {
            getSdrDragView().RotateMarkedPoints(DragStat().GetRef1(),nAngle);
        }
        else if (IsDraggingGluePoints())
        {
            getSdrDragView().RotateMarkedGluePoints(DragStat().GetRef1(),nAngle,bCopy);
        }
        else
        {
            getSdrDragView().RotateMarkedObj(DragStat().GetRef1(),nAngle,bCopy);
        }
    }
    return true;
}
 
PointerStyle SdrDragRotate::GetSdrDragPointer() const
{
    return PointerStyle::Rotate;
}
 
 
SdrDragShear::SdrDragShear(SdrDragView& rNewView, bool bSlant1)
:   SdrDragMethod(rNewView),
    aFact(1,1),
    nAngle0(0),
    nAngle(0),
    nTan(0.0),
    bVertical(false),
    bResize(false),
    bUpSideDown(false),
    bSlant(bSlant1)
{
}
 
OUString SdrDragShear::GetSdrDragComment() const
{
    OUString aStr = ImpGetDescriptionStr(STR_DragMethShear) +
        " (";
 
    Degree100 nTmpAngle(nAngle);
 
    if(bUpSideDown)
        nTmpAngle += 18000_deg100;
 
    nTmpAngle = NormAngle18000(nTmpAngle);
 
    aStr += SdrModel::GetAngleString(nTmpAngle) + ")";
 
    if(getSdrDragView().IsDragWithCopy())
        aStr += SvxResId(STR_EditWithCopy);
    return aStr;
}
 
bool SdrDragShear::BeginSdrDrag()
{
    SdrHdlKind eRefHdl=SdrHdlKind::Move;
    SdrHdl* pRefHdl=nullptr;
 
    switch (GetDragHdlKind())
    {
        case SdrHdlKind::Upper: eRefHdl=SdrHdlKind::Lower; break;
        case SdrHdlKind::Lower: eRefHdl=SdrHdlKind::Upper; break;
        case SdrHdlKind::Left : eRefHdl=SdrHdlKind::Right; bVertical=true; break;
        case SdrHdlKind::Right: eRefHdl=SdrHdlKind::Left ; bVertical=true; break;
        default: break;
    }
 
    if (eRefHdl!=SdrHdlKind::Move)
        pRefHdl=GetHdlList().GetHdl(eRefHdl);
 
    if (pRefHdl!=nullptr)
    {
        DragStat().SetRef1(pRefHdl->GetPos());
        nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1());
    }
    else
    {
        OSL_FAIL("SdrDragShear::BeginSdrDrag(): No reference point handle for shearing found.");
        return false;
    }
 
    Show();
    return true;
}
 
basegfx::B2DHomMatrix SdrDragShear::getCurrentTransformation() const
{
    basegfx::B2DHomMatrix aRetval(basegfx::utils::createTranslateB2DHomMatrix(
        -DragStat().GetRef1().X(), -DragStat().GetRef1().Y()));
 
    if (bResize)
    {
        if (bVertical)
        {
            aRetval.scale(double(aFact), 1.0);
            aRetval.shearY(-nTan);
        }
        else
        {
            aRetval.scale(1.0, double(aFact));
            aRetval.shearX(-nTan);
        }
    }
 
    aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y());
 
    return aRetval;
}
 
void SdrDragShear::MoveSdrDrag(const Point& rPnt)
{
    if (!DragStat().CheckMinMoved(rPnt))
        return;
 
    bResize=!getSdrDragView().IsOrtho();
    Degree100 nSA(0);
 
    if (getSdrDragView().IsAngleSnapEnabled())
        nSA=getSdrDragView().GetSnapAngle();
 
    Point aP0(DragStat().GetStart());
    Point aPnt(rPnt);
    Fraction aNewFract(1,1);
 
    // if angle snapping not activated, snap to raster (except when using slant)
    if (nSA==0_deg100 && !bSlant)
        aPnt=GetSnapPos(aPnt);
 
    if (!bSlant && !bResize)
    { // shear, but no resize
        if (bVertical)
            aPnt.setX(aP0.X() );
        else
            aPnt.setY(aP0.Y() );
    }
 
    Point aRef(DragStat().GetRef1());
    Point aDif(aPnt-aRef);
 
    Degree100 nNewAngle(0);
 
    if (bSlant)
    {
        nNewAngle=NormAngle18000(-(GetAngle(aDif)-nAngle0));
 
        if (bVertical)
            nNewAngle=NormAngle18000(-nNewAngle);
    }
    else
    {
        if (bVertical)
            nNewAngle=NormAngle18000(GetAngle(aDif));
        else
            nNewAngle=NormAngle18000(-(GetAngle(aDif)-9000_deg100));
 
        if (nNewAngle<Degree100(-9000) || nNewAngle>9000_deg100)
            nNewAngle=NormAngle18000(nNewAngle+18000_deg100);
 
        if (bResize)
        {
            Point aPt2(aPnt);
 
            if (nSA!=0_deg100)
                aPt2=GetSnapPos(aPnt); // snap this one in any case
 
            if (bVertical)
            {
                aNewFract=Fraction(aPt2.X()-aRef.X(),aP0.X()-aRef.X());
            }
            else
            {
                aNewFract=Fraction(aPt2.Y()-aRef.Y(),aP0.Y()-aRef.Y());
            }
        }
    }
 
    bool bNeg=nNewAngle<0_deg100;
 
    if (bNeg)
        nNewAngle=-nNewAngle;
 
    if (nSA)
    { // angle snapping
        nNewAngle += nSA / 2_deg100;
        nNewAngle /= nSA;
        nNewAngle *= nSA;
    }
 
    nNewAngle=NormAngle36000(nNewAngle);
    bUpSideDown=nNewAngle>9000_deg100 && nNewAngle<27000_deg100;
 
    if (bSlant)
    { // calculate resize for slant
        // when angle snapping is activated, disable 89 degree limit
        Degree100 nTmpAngle=nNewAngle;
        if (bUpSideDown) nNewAngle -= 18000_deg100;
        if (bNeg) nTmpAngle=-nTmpAngle;
        bResize=true;
        aNewFract = cos(toRadians(nTmpAngle));
        aFact.ReduceInaccurate(10); // three decimals should be enough
    }
 
    if (nNewAngle > 8900_deg100)
        nNewAngle = 8900_deg100;
 
    if (bNeg)
        nNewAngle=-nNewAngle;
 
    if (nAngle!=nNewAngle || aFact!=aNewFract)
    {
        nAngle=nNewAngle;
        aFact=aNewFract;
        double a = toRadians(nAngle);
        double nTan1=tan(a); // calculate now, so as little time as possible passes between Hide() and Show()
        Hide();
        nTan=nTan1;
        DragStat().NextMove(rPnt);
        Show();
    }
}
 
void SdrDragShear::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
{
    if (bResize)
    {
        if (bVertical)
        {
            rTarget.Resize(DragStat().GetRef1(),aFact,Fraction(1,1));
        }
        else
        {
            rTarget.Resize(DragStat().GetRef1(),Fraction(1,1),aFact);
        }
    }
 
    if (nAngle)
    {
        rTarget.Shear(DragStat().GetRef1(), nAngle, nTan, bVertical);
    }
}
 
bool SdrDragShear::EndSdrDrag(bool bCopy)
{
    Hide();
 
    if (bResize && aFact==Fraction(1,1))
        bResize=false;
 
    if (nAngle || bResize)
    {
        if (nAngle && bResize)
        {
            OUString aStr = ImpGetDescriptionStr(STR_EditShear);
 
            if (bCopy)
                aStr += SvxResId(STR_EditWithCopy);
 
            getSdrDragView().BegUndo(aStr);
        }
 
        if (bResize)
        {
            if (bVertical)
            {
                getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),aFact,Fraction(1,1),bCopy);
            }
            else
            {
                getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),Fraction(1,1),aFact,bCopy);
            }
 
            bCopy=false;
        }
 
        if (nAngle)
        {
            getSdrDragView().ShearMarkedObj(DragStat().GetRef1(),nAngle,bVertical,bCopy);
        }
 
        if (nAngle && bResize)
            getSdrDragView().EndUndo();
 
        return true;
    }
 
    return false;
}
 
PointerStyle SdrDragShear::GetSdrDragPointer() const
{
    if (bVertical)
        return PointerStyle::VShear;
    else
        return PointerStyle::HShear;
}
 
 
void SdrDragMirror::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
{
    if(bMirrored)
    {
        rTarget.Mirror(DragStat().GetRef1(), DragStat().GetRef2());
    }
}
 
SdrDragMirror::SdrDragMirror(SdrDragView& rNewView)
:   SdrDragMethod(rNewView),
    nAngle(0),
    bMirrored(false),
    bSide0(false)
{
}
 
bool SdrDragMirror::ImpCheckSide(const Point& rPnt) const
{
    Degree100 nAngle1=GetAngle(rPnt-DragStat().GetRef1());
    nAngle1-=nAngle;
    nAngle1=NormAngle36000(nAngle1);
 
    return nAngle1<18000_deg100;
}
 
OUString SdrDragMirror::GetSdrDragComment() const
{
    OUString aStr;
    if (aDif.X()==0)
        aStr = ImpGetDescriptionStr(STR_DragMethMirrorHori);
    else if (aDif.Y()==0)
        aStr = ImpGetDescriptionStr(STR_DragMethMirrorVert);
    else if (std::abs(aDif.X()) == std::abs(aDif.Y()))
        aStr = ImpGetDescriptionStr(STR_DragMethMirrorDiag);
    else
        aStr = ImpGetDescriptionStr(STR_DragMethMirrorFree);
 
    if (getSdrDragView().IsDragWithCopy())
        aStr+=SvxResId(STR_EditWithCopy);
    return aStr;
}
 
bool SdrDragMirror::BeginSdrDrag()
{
    SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1);
    SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2);
 
    if (pH1!=nullptr && pH2!=nullptr)
    {
        DragStat().SetRef1(pH1->GetPos());
        DragStat().SetRef2(pH2->GetPos());
        Ref1()=pH1->GetPos();
        Ref2()=pH2->GetPos();
        aDif=pH2->GetPos()-pH1->GetPos();
        bool b90=(aDif.X()==0) || aDif.Y()==0;
        bool b45=b90 || (std::abs(aDif.X()) == std::abs(aDif.Y()));
        nAngle=NormAngle36000(GetAngle(aDif));
 
        if (!getSdrDragView().IsMirrorAllowed() && !b45)
            return false; // free choice of axis angle not allowed
 
        if (!getSdrDragView().IsMirrorAllowed() && !b90)
            return false;  // 45 degrees not allowed either
 
        bSide0=ImpCheckSide(DragStat().GetStart());
        Show();
        return true;
    }
    else
    {
        OSL_FAIL("SdrDragMirror::BeginSdrDrag(): Axis of reflection not found.");
        return false;
    }
}
 
basegfx::B2DHomMatrix SdrDragMirror::getCurrentTransformation() const
{
    basegfx::B2DHomMatrix aRetval;
 
    if (bMirrored)
    {
        const double fDeltaX(DragStat().GetRef2().X() - DragStat().GetRef1().X());
        const double fDeltaY(DragStat().GetRef2().Y() - DragStat().GetRef1().Y());
        const double fRotation(atan2(fDeltaY, fDeltaX));
 
        aRetval = basegfx::utils::createTranslateB2DHomMatrix(-DragStat().GetRef1().X(), -DragStat().GetRef1().Y());
        aRetval.rotate(-fRotation);
        aRetval.scale(1.0, -1.0);
        aRetval.rotate(fRotation);
        aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y());
    }
 
    return aRetval;
}
 
void SdrDragMirror::MoveSdrDrag(const Point& rPnt)
{
    if (!DragStat().CheckMinMoved(rPnt))
        return;
 
    bool bNewSide=ImpCheckSide(rPnt);
    bool bNewMirrored=bSide0!=bNewSide;
 
    if (bMirrored!=bNewMirrored)
    {
        Hide();
        bMirrored=bNewMirrored;
        DragStat().NextMove(rPnt);
        Show();
    }
}
 
bool SdrDragMirror::EndSdrDrag(bool bCopy)
{
    Hide();
 
    if (bMirrored)
    {
        getSdrDragView().MirrorMarkedObj(DragStat().GetRef1(),DragStat().GetRef2(),bCopy);
    }
 
    return true;
}
 
PointerStyle SdrDragMirror::GetSdrDragPointer() const
{
    return PointerStyle::Mirror;
}
 
 
SdrDragGradient::SdrDragGradient(SdrDragView& rNewView, bool bGrad)
:   SdrDragMethod(rNewView),
    pIAOHandle(nullptr),
    bIsGradient(bGrad)
{
}
 
OUString SdrDragGradient::GetSdrDragComment() const
{
    if(IsGradient())
        return ImpGetDescriptionStr(STR_DragMethGradient);
    else
        return ImpGetDescriptionStr(STR_DragMethTransparence);
}
 
bool SdrDragGradient::BeginSdrDrag()
{
    bool bRetval(false);
 
    pIAOHandle = static_cast<SdrHdlGradient*>(GetHdlList().GetHdl(IsGradient() ? SdrHdlKind::Gradient : SdrHdlKind::Transparence));
 
    if(pIAOHandle)
    {
        // save old values
        DragStat().SetRef1( pIAOHandle->GetPos() );
        DragStat().SetRef2( pIAOHandle->Get2ndPos() );
 
        // what was hit?
        bool bHit(false);
        SdrHdlColor* pColHdl = pIAOHandle->GetColorHdl1();
 
        // init handling flags
        pIAOHandle->SetMoveSingleHandle(false);
        pIAOHandle->SetMoveFirstHandle(false);
 
        // test first color handle
        if(pColHdl)
        {
            basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y());
 
            if(pColHdl->getOverlayObjectList().isHitLogic(aPosition))
            {
                bHit = true;
                pIAOHandle->SetMoveSingleHandle(true);
                pIAOHandle->SetMoveFirstHandle(true);
            }
        }
 
        // test second color handle
        pColHdl = pIAOHandle->GetColorHdl2();
 
        if(!bHit && pColHdl)
        {
            basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y());
 
            if(pColHdl->getOverlayObjectList().isHitLogic(aPosition))
            {
                bHit = true;
                pIAOHandle->SetMoveSingleHandle(true);
            }
        }
 
        // test gradient handle itself
        if(!bHit)
        {
            basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y());
 
            if(pIAOHandle->getOverlayObjectList().isHitLogic(aPosition))
            {
                bHit = true;
            }
        }
 
        // everything up and running :o}
        bRetval = bHit;
    }
    else
    {
        OSL_FAIL("SdrDragGradient::BeginSdrDrag(): IAOGradient not found.");
    }
 
    return bRetval;
}
 
void SdrDragGradient::MoveSdrDrag(const Point& rPnt)
{
    if(!(pIAOHandle && DragStat().CheckMinMoved(rPnt)))
        return;
 
    DragStat().NextMove(rPnt);
 
    // Do the Move here!!! DragStat().GetStart()
    Point aMoveDiff = rPnt - DragStat().GetStart();
 
    if(pIAOHandle->IsMoveSingleHandle())
    {
        if(pIAOHandle->IsMoveFirstHandle())
        {
            pIAOHandle->SetPos(DragStat().GetRef1() + aMoveDiff);
            if(pIAOHandle->GetColorHdl1())
                pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1() + aMoveDiff);
        }
        else
        {
            pIAOHandle->Set2ndPos(DragStat().GetRef2() + aMoveDiff);
            if(pIAOHandle->GetColorHdl2())
                pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2() + aMoveDiff);
        }
    }
    else
    {
        pIAOHandle->SetPos(DragStat().GetRef1() + aMoveDiff);
        pIAOHandle->Set2ndPos(DragStat().GetRef2() + aMoveDiff);
 
        if(pIAOHandle->GetColorHdl1())
            pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1() + aMoveDiff);
 
        if(pIAOHandle->GetColorHdl2())
            pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2() + aMoveDiff);
    }
 
    // new state
    const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
    pIAOHandle->FromIAOToItem(rMarkList.GetMark(0)->GetMarkedSdrObj(), false, false);
}
 
bool SdrDragGradient::EndSdrDrag(bool /*bCopy*/)
{
    Ref1() = pIAOHandle->GetPos();
    Ref2() = pIAOHandle->Get2ndPos();
 
    // new state
    const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
    pIAOHandle->FromIAOToItem(rMarkList.GetMark(0)->GetMarkedSdrObj(), true, true);
 
    return true;
}
 
void SdrDragGradient::CancelSdrDrag()
{
    // restore old values
    pIAOHandle->SetPos(DragStat().GetRef1());
    pIAOHandle->Set2ndPos(DragStat().GetRef2());
 
    if(pIAOHandle->GetColorHdl1())
        pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1());
 
    if(pIAOHandle->GetColorHdl2())
        pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2());
 
    // new state
    const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
    pIAOHandle->FromIAOToItem(rMarkList.GetMark(0)->GetMarkedSdrObj(), true, false);
}
 
PointerStyle SdrDragGradient::GetSdrDragPointer() const
{
    return PointerStyle::RefHand;
}
 
 
SdrDragCrook::SdrDragCrook(SdrDragView& rNewView)
:   SdrDragMethod(rNewView),
    aFact(1,1),
    bContortionAllowed(false),
    bNoContortionAllowed(false),
    bContortion(false),
    bResizeAllowed(false),
    bResize(false),
    bRotateAllowed(false),
    bRotate(false),
    bVertical(false),
    bValid(false),
    bLft(false),
    bRgt(false),
    bUpr(false),
    bLwr(false),
    bAtCenter(false),
    nAngle(0),
    nMarkSize(0),
    eMode(SdrCrookMode::Rotate)
{
}
 
OUString SdrDragCrook::GetSdrDragComment() const
{
    OUString aStr = ImpGetDescriptionStr(!bContortion ? STR_DragMethCrook : STR_DragMethCrookContortion);
 
    if(bValid)
    {
        aStr += " (";
 
        sal_Int32 nVal(nAngle);
 
        if(bAtCenter)
            nVal *= 2;
 
        nVal = std::abs(nVal);
        aStr += SdrModel::GetAngleString(Degree100(nVal)) + ")";
    }
 
    if(getSdrDragView().IsDragWithCopy())
        aStr += SvxResId(STR_EditWithCopy);
    return aStr;
}
 
// These defines parametrize the created raster
// for interactions
#define DRAG_CROOK_RASTER_MINIMUM   (4)
#define DRAG_CROOK_RASTER_MAXIMUM   (15)
#define DRAG_CROOK_RASTER_DISTANCE  (30)
 
static basegfx::B2DPolyPolygon impCreateDragRaster(SdrPageView const & rPageView, const tools::Rectangle& rMarkRect)
{
    basegfx::B2DPolyPolygon aRetval;
 
    if(rPageView.PageWindowCount())
    {
        OutputDevice& rOut = rPageView.GetPageWindow(0)->GetPaintWindow().GetOutputDevice();
        tools::Rectangle aPixelSize = rOut.LogicToPixel(rMarkRect);
        sal_uInt32 nHorDiv(aPixelSize.GetWidth() / DRAG_CROOK_RASTER_DISTANCE);
        sal_uInt32 nVerDiv(aPixelSize.GetHeight() / DRAG_CROOK_RASTER_DISTANCE);
 
        if(nHorDiv > DRAG_CROOK_RASTER_MAXIMUM)
            nHorDiv = DRAG_CROOK_RASTER_MAXIMUM;
        if(nHorDiv < DRAG_CROOK_RASTER_MINIMUM)
            nHorDiv = DRAG_CROOK_RASTER_MINIMUM;
 
        if(nVerDiv > DRAG_CROOK_RASTER_MAXIMUM)
            nVerDiv = DRAG_CROOK_RASTER_MAXIMUM;
        if(nVerDiv < DRAG_CROOK_RASTER_MINIMUM)
            nVerDiv = DRAG_CROOK_RASTER_MINIMUM;
 
        const double fXLen(rMarkRect.GetWidth() / static_cast<double>(nHorDiv));
        const double fYLen(rMarkRect.GetHeight() / static_cast<double>(nVerDiv));
        double fYPos(rMarkRect.Top());
        sal_uInt32 a, b;
 
        for(a = 0; a <= nVerDiv; a++)
        {
            // horizontal lines
            for(b = 0; b < nHorDiv; b++)
            {
                basegfx::B2DPolygon aHorLineSegment;
 
                const double fNewX(rMarkRect.Left() + (b * fXLen));
                aHorLineSegment.append(basegfx::B2DPoint(fNewX, fYPos));
                aHorLineSegment.appendBezierSegment(
                    basegfx::B2DPoint(fNewX + (fXLen * (1.0 / 3.0)), fYPos),
                    basegfx::B2DPoint(fNewX + (fXLen * (2.0 / 3.0)), fYPos),
                    basegfx::B2DPoint(fNewX + fXLen, fYPos));
                aRetval.append(aHorLineSegment);
            }
 
            // increments
            fYPos += fYLen;
        }
 
        double fXPos(rMarkRect.Left());
 
        for(a = 0; a <= nHorDiv; a++)
        {
            // vertical lines
            for(b = 0; b < nVerDiv; b++)
            {
                basegfx::B2DPolygon aVerLineSegment;
 
                const double fNewY(rMarkRect.Top() + (b * fYLen));
                aVerLineSegment.append(basegfx::B2DPoint(fXPos, fNewY));
                aVerLineSegment.appendBezierSegment(
                    basegfx::B2DPoint(fXPos, fNewY + (fYLen * (1.0 / 3.0))),
                    basegfx::B2DPoint(fXPos, fNewY + (fYLen * (2.0 / 3.0))),
                    basegfx::B2DPoint(fXPos, fNewY + fYLen));
                aRetval.append(aVerLineSegment);
            }
 
            // increments
            fXPos += fXLen;
        }
    }
 
    return aRetval;
}
 
void SdrDragCrook::createSdrDragEntries()
{
    // Add extended frame raster first, so it will be behind objects
    if(getSdrDragView().GetSdrPageView())
    {
        const basegfx::B2DPolyPolygon aDragRaster(impCreateDragRaster(*getSdrDragView().GetSdrPageView(), GetMarkedRect()));
 
        if(aDragRaster.count())
        {
            addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragRaster)));
        }
    }
 
    // call parent
    SdrDragMethod::createSdrDragEntries();
}
 
bool SdrDragCrook::BeginSdrDrag()
{
    bContortionAllowed=getSdrDragView().IsCrookAllowed();
    bNoContortionAllowed=getSdrDragView().IsCrookAllowed(true);
    bResizeAllowed=getSdrDragView().IsResizeAllowed();
    bRotateAllowed=getSdrDragView().IsRotateAllowed();
 
    if (bContortionAllowed || bNoContortionAllowed)
    {
        bVertical=(GetDragHdlKind()==SdrHdlKind::Lower || GetDragHdlKind()==SdrHdlKind::Upper);
        aMarkRect=GetMarkedRect();
        aMarkCenter=aMarkRect.Center();
        nMarkSize=bVertical ? (aMarkRect.GetHeight()-1) : (aMarkRect.GetWidth()-1);
        aCenter=aMarkCenter;
        aStart=DragStat().GetStart();
        Show();
        return true;
    }
    else
    {
        return false;
    }
}
 
void SdrDragCrook::MovAllPoints(basegfx::B2DPolyPolygon& rTarget)
{
    SdrPageView* pPV = getSdrDragView().GetSdrPageView();
 
    if(!pPV)
        return;
 
    XPolyPolygon aTempPolyPoly(rTarget);
 
    if (pPV->HasMarkedObjPageView())
    {
        sal_uInt16 nPolyCount=aTempPolyPoly.Count();
 
        if (!bContortion && !getSdrDragView().IsNoDragXorPolys())
        {
            sal_uInt16 n1st=0,nLast=0;
            Point aC(aCenter);
 
            while (n1st<nPolyCount)
            {
                nLast=n1st;
                while (nLast<nPolyCount && aTempPolyPoly[nLast].GetPointCount()!=0) nLast++;
                tools::Rectangle aBound(aTempPolyPoly[n1st].GetBoundRect());
                sal_uInt16 i;
 
                for (i=n1st+1; i<nLast; i++)
                {
                    aBound.Union(aTempPolyPoly[n1st].GetBoundRect());
                }
 
                Point aCtr0(aBound.Center());
                Point aCtr1(aCtr0);
 
                if (bResize)
                {
                    Fraction aFact1(1,1);
 
                    if (bVertical)
                    {
                        ResizePoint(aCtr1,aC,aFact1,aFact);
                    }
                    else
                    {
                        ResizePoint(aCtr1,aC,aFact,aFact1);
                    }
                }
 
                bool bRotOk=false;
                double nSin=0,nCos=0;
 
                if (aRad.X()!=0 && aRad.Y()!=0)
                {
                    bRotOk=bRotate;
 
                    switch (eMode)
                    {
                        case SdrCrookMode::Rotate : CrookRotateXPoint (aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical);           break;
                        case SdrCrookMode::Slant  : CrookSlantXPoint  (aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical);           break;
                        case SdrCrookMode::Stretch: CrookStretchXPoint(aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical,aMarkRect); break;
                    } // switch
                }
 
                aCtr1-=aCtr0;
 
                for (i=n1st; i<nLast; i++)
                {
                    if (bRotOk)
                    {
                        RotateXPoly(aTempPolyPoly[i],aCtr0,nSin,nCos);
                    }
 
                    aTempPolyPoly[i].Move(aCtr1.X(),aCtr1.Y());
                }
 
                n1st=nLast+1;
            }
        }
        else
        {
            sal_uInt16 i,j;
 
            for (j=0; j<nPolyCount; j++)
            {
                XPolygon& aPol=aTempPolyPoly[j];
                sal_uInt16 nPointCount=aPol.GetPointCount();
                i=0;
 
                while (i<nPointCount)
                {
                    Point* pPnt=&aPol[i];
                    Point* pC1=nullptr;
                    Point* pC2=nullptr;
 
                    if (i+1<nPointCount && aPol.IsControl(i))
                    { // control point on the left
                        pC1=pPnt;
                        i++;
                        pPnt=&aPol[i];
                    }
 
                    i++;
 
                    if (i<nPointCount && aPol.IsControl(i))
                    { // control point on the right
                        pC2=&aPol[i];
                        i++;
                    }
 
                    MovCrookPoint(*pPnt,pC1,pC2);
                }
            }
        }
    }
 
    rTarget = aTempPolyPoly.getB2DPolyPolygon();
}
 
void SdrDragCrook::MovCrookPoint(Point& rPnt, Point* pC1, Point* pC2)
{
    bool bVert=bVertical;
    bool bC1=pC1!=nullptr;
    bool bC2=pC2!=nullptr;
    Point aC(aCenter);
 
    if (bResize)
    {
        Fraction aFact1(1,1);
 
        if (bVert)
        {
            ResizePoint(rPnt,aC,aFact1,aFact);
 
            if (bC1)
                ResizePoint(*pC1,aC,aFact1,aFact);
 
            if (bC2)
                ResizePoint(*pC2,aC,aFact1,aFact);
        }
        else
        {
            ResizePoint(rPnt,aC,aFact,aFact1);
 
            if (bC1)
                ResizePoint(*pC1,aC,aFact,aFact1);
 
            if (bC2)
                ResizePoint(*pC2,aC,aFact,aFact1);
        }
    }
 
    if (aRad.X()!=0 && aRad.Y()!=0)
    {
        double nSin,nCos;
 
        switch (eMode)
        {
            case SdrCrookMode::Rotate : CrookRotateXPoint (rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert);           break;
            case SdrCrookMode::Slant  : CrookSlantXPoint  (rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert);           break;
            case SdrCrookMode::Stretch: CrookStretchXPoint(rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert,aMarkRect); break;
        } // switch
    }
}
 
void SdrDragCrook::MoveSdrDrag(const Point& rPnt)
{
    if (!DragStat().CheckMinMoved(rPnt))
        return;
 
    bool bNewMoveOnly=getSdrDragView().IsMoveOnlyDragging();
    bAtCenter=false;
    SdrCrookMode eNewMode=getSdrDragView().GetCrookMode();
    bool bNewContortion=!bNewMoveOnly && ((bContortionAllowed && !getSdrDragView().IsCrookNoContortion()) || !bNoContortionAllowed);
    bResize=!getSdrDragView().IsOrtho() && bResizeAllowed && !bNewMoveOnly;
    bool bNewRotate=bRotateAllowed && !bNewContortion && !bNewMoveOnly && eNewMode==SdrCrookMode::Rotate;
 
    Point aPnt(GetSnapPos(rPnt));
 
    Point aNewCenter(aMarkCenter.X(),aStart.Y());
 
    if (bVertical)
    {
        aNewCenter.setX(aStart.X() );
        aNewCenter.setY(aMarkCenter.Y() );
    }
 
    if (!getSdrDragView().IsCrookAtCenter())
    {
        switch (GetDragHdlKind())
        {
            case SdrHdlKind::UpperLeft: aNewCenter.setX(aMarkRect.Right() );  bLft=true; break;
            case SdrHdlKind::Upper: aNewCenter.setY(aMarkRect.Bottom() ); bUpr=true; break;
            case SdrHdlKind::UpperRight: aNewCenter.setX(aMarkRect.Left() );   bRgt=true; break;
            case SdrHdlKind::Left : aNewCenter.setX(aMarkRect.Right() );  bLft=true; break;
            case SdrHdlKind::Right: aNewCenter.setX(aMarkRect.Left() );   bRgt=true; break;
            case SdrHdlKind::LowerLeft: aNewCenter.setX(aMarkRect.Right() );  bLft=true; break;
            case SdrHdlKind::Lower: aNewCenter.setY(aMarkRect.Top() );    bLwr=true; break;
            case SdrHdlKind::LowerRight: aNewCenter.setX(aMarkRect.Left() );   bRgt=true; break;
            default: bAtCenter=true;
        }
    }
    else
        bAtCenter=true;
 
    Fraction aNewFract(1,1);
    tools::Long dx1=aPnt.X()-aNewCenter.X();
    tools::Long dy1=aPnt.Y()-aNewCenter.Y();
    bValid=bVertical ? dx1!=0 : dy1!=0;
 
    if (bValid)
    {
        if (bVertical)
            bValid = std::abs(dx1)*100>std::abs(dy1);
        else
            bValid = std::abs(dy1)*100>std::abs(dx1);
    }
 
    tools::Long nNewRad=0;
    nAngle=0_deg100;
 
    if (bValid)
    {
        double a=0; // slope of the radius
        Degree100 nPntAngle(0);
 
        if (bVertical)
        {
            a=static_cast<double>(dy1)/static_cast<double>(dx1); // slope of the radius
            nNewRad=(static_cast<tools::Long>(dy1*a)+dx1) /2;
            aNewCenter.AdjustX(nNewRad );
            nPntAngle=GetAngle(aPnt-aNewCenter);
        }
        else
        {
            a=static_cast<double>(dx1)/static_cast<double>(dy1); // slope of the radius
            nNewRad=(static_cast<tools::Long>(dx1*a)+dy1) /2;
            aNewCenter.AdjustY(nNewRad );
            nPntAngle=GetAngle(aPnt-aNewCenter)-9000_deg100;
        }
 
        if (!bAtCenter)
        {
            if (nNewRad<0)
            {
                if (bRgt) nPntAngle += 18000_deg100;
                if (bLft) nPntAngle = 18000_deg100 - nPntAngle;
                if (bLwr) nPntAngle =- nPntAngle;
            }
            else
            {
                if (bRgt) nPntAngle = -nPntAngle;
                if (bUpr) nPntAngle = 18000_deg100 - nPntAngle;
                if (bLwr) nPntAngle += 18000_deg100;
            }
 
            nPntAngle=NormAngle36000(nPntAngle);
        }
        else
        {
            if (nNewRad<0) nPntAngle += 18000_deg100;
            if (bVertical) nPntAngle = 18000_deg100 - nPntAngle;
            nPntAngle = NormAngle18000(nPntAngle);
            nPntAngle = abs(nPntAngle);
        }
 
        double nCircumference = 2 * std::abs(nNewRad) * M_PI;
 
        if (bResize)
        {
            tools::Long nMul=static_cast<tools::Long>(nCircumference * NormAngle36000(nPntAngle).get() / 36000.0);
 
            if (bAtCenter)
                nMul*=2;
 
            aNewFract=Fraction(nMul,nMarkSize);
            nAngle=nPntAngle;
        }
        else
        {
            nAngle = Degree100(static_cast<tools::Long>((nMarkSize*360/nCircumference)*100)/2);
 
            if (nAngle==0_deg100)
                bValid=false;
        }
    }
 
    if (nAngle==0_deg100 || nNewRad==0)
        bValid=false;
 
    if (!bValid)
        nNewRad=0;
 
    if (!bValid && bResize)
    {
        tools::Long nMul=bVertical ? dy1 : dx1;
 
        if (bLft || bUpr)
            nMul=-nMul;
 
        tools::Long nDiv=nMarkSize;
 
        if (bAtCenter)
        {
            nMul*=2;
            nMul = std::abs(nMul);
        }
 
        aNewFract=Fraction(nMul,nDiv);
    }
 
    if (aNewCenter==aCenter && bNewContortion==bContortion && aNewFract==aFact &&
        bNewMoveOnly == getMoveOnly() && bNewRotate==bRotate && eNewMode==eMode)
        return;
 
    Hide();
    setMoveOnly(bNewMoveOnly);
    bRotate=bNewRotate;
    eMode=eNewMode;
    bContortion=bNewContortion;
    aCenter=aNewCenter;
    aFact=aNewFract;
    aRad=Point(nNewRad,nNewRad);
    bResize=aFact!=Fraction(1,1) && aFact.GetDenominator()!=0 && aFact.IsValid();
    DragStat().NextMove(aPnt);
    Show();
}
 
void SdrDragCrook::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
{
    const bool bDoResize(aFact!=Fraction(1,1));
    const bool bDoCrook(aCenter!=aMarkCenter && aRad.X()!=0 && aRad.Y()!=0);
 
    if (!(bDoCrook || bDoResize))
        return;
 
    if (bDoResize)
    {
        Fraction aFact1(1,1);
 
        if (bContortion)
        {
            if (bVertical)
            {
                rTarget.Resize(aCenter,aFact1,aFact);
            }
            else
            {
                rTarget.Resize(aCenter,aFact,aFact1);
            }
        }
        else
        {
            Point aCtr0(rTarget.GetSnapRect().Center());
            Point aCtr1(aCtr0);
 
            if (bVertical)
            {
                ResizePoint(aCtr1,aCenter,aFact1,aFact);
            }
            else
            {
                ResizePoint(aCtr1,aCenter,aFact,aFact1);
            }
 
            Size aSiz(aCtr1.X()-aCtr0.X(),aCtr1.Y()-aCtr0.Y());
 
            rTarget.Move(aSiz);
        }
    }
 
    if (bDoCrook)
    {
        const tools::Rectangle aLocalMarkRect(getSdrDragView().GetMarkedObjRect());
        const bool bLocalRotate(!bContortion && eMode == SdrCrookMode::Rotate && getSdrDragView().IsRotateAllowed());
 
        SdrEditView::ImpCrookObj(&rTarget,aCenter,aRad,eMode,bVertical,!bContortion,bLocalRotate,aLocalMarkRect);
    }
}
 
void SdrDragCrook::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget)
{
    // use helper derived from old stuff
    MovAllPoints(rTarget);
}
 
bool SdrDragCrook::EndSdrDrag(bool bCopy)
{
    Hide();
 
    if (bResize && aFact==Fraction(1,1))
        bResize=false;
 
    const bool bUndo = getSdrDragView().IsUndoEnabled();
 
    bool bDoCrook=aCenter!=aMarkCenter && aRad.X()!=0 && aRad.Y()!=0;
 
    if (bDoCrook || bResize)
    {
        if (bResize && bUndo)
        {
            OUString aStr = ImpGetDescriptionStr(!bContortion?STR_EditCrook:STR_EditCrookContortion);
 
            if (bCopy)
                aStr += SvxResId(STR_EditWithCopy);
 
            getSdrDragView().BegUndo(aStr);
        }
 
        if (bResize)
        {
            Fraction aFact1(1,1);
 
            if (bContortion)
            {
                if (bVertical)
                    getSdrDragView().ResizeMarkedObj(aCenter,aFact1,aFact,bCopy);
                else
                    getSdrDragView().ResizeMarkedObj(aCenter,aFact,aFact1,bCopy);
            }
            else
            {
                if (bCopy)
                    getSdrDragView().CopyMarkedObj();
 
                const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
                const size_t nMarkCount=rMarkList.GetMarkCount();
 
                for (size_t nm=0; nm<nMarkCount; ++nm)
                {
                    SdrMark* pM=rMarkList.GetMark(nm);
                    SdrObject* pO=pM->GetMarkedSdrObj();
                    Point aCtr0(pO->GetSnapRect().Center());
                    Point aCtr1(aCtr0);
 
                    if (bVertical)
                        ResizePoint(aCtr1,aCenter,aFact1,aFact);
                    else
                        ResizePoint(aCtr1,aCenter,aFact,aFact1);
 
                    Size aSiz(aCtr1.X()-aCtr0.X(),aCtr1.Y()-aCtr0.Y());
                    if( bUndo )
                        AddUndo(getSdrDragView().GetModel().GetSdrUndoFactory().CreateUndoMoveObject(*pO,aSiz));
                    pO->Move(aSiz);
                }
            }
 
            bCopy=false;
        }
 
        if (bDoCrook)
        {
            getSdrDragView().CrookMarkedObj(aCenter,aRad,eMode,bVertical,!bContortion,bCopy);
        }
 
        if (bResize && bUndo)
            getSdrDragView().EndUndo();
 
        return true;
    }
 
    return false;
}
 
PointerStyle SdrDragCrook::GetSdrDragPointer() const
{
    return PointerStyle::Crook;
}
 
 
SdrDragDistort::SdrDragDistort(SdrDragView& rNewView)
:   SdrDragMethod(rNewView),
    nPolyPt(0),
    bContortionAllowed(false),
    bNoContortionAllowed(false),
    bContortion(false)
{
}
 
OUString SdrDragDistort::GetSdrDragComment() const
{
    OUString aStr = ImpGetDescriptionStr(STR_DragMethDistort)
            + " (x="
            + getSdrDragView().GetModel().GetMetricString(DragStat().GetDX())
            + " y="
            + getSdrDragView().GetModel().GetMetricString(DragStat().GetDY())
            + ")";
 
    if(getSdrDragView().IsDragWithCopy())
        aStr += SvxResId(STR_EditWithCopy);
    return aStr;
}
 
void SdrDragDistort::createSdrDragEntries()
{
    // Add extended frame raster first, so it will be behind objects
    if(getSdrDragView().GetSdrPageView())
    {
        const basegfx::B2DPolyPolygon aDragRaster(impCreateDragRaster(*getSdrDragView().GetSdrPageView(), GetMarkedRect()));
 
        if(aDragRaster.count())
        {
            addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragRaster)));
        }
    }
 
    // call parent
    SdrDragMethod::createSdrDragEntries();
}
 
bool SdrDragDistort::BeginSdrDrag()
{
    bContortionAllowed=getSdrDragView().IsDistortAllowed();
    bNoContortionAllowed=getSdrDragView().IsDistortAllowed(true);
 
    if (bContortionAllowed || bNoContortionAllowed)
    {
        SdrHdlKind eKind=GetDragHdlKind();
        nPolyPt=0xFFFF;
 
        if (eKind==SdrHdlKind::UpperLeft) nPolyPt=0;
        if (eKind==SdrHdlKind::UpperRight) nPolyPt=1;
        if (eKind==SdrHdlKind::LowerRight) nPolyPt=2;
        if (eKind==SdrHdlKind::LowerLeft) nPolyPt=3;
        if (nPolyPt>3) return false;
 
        aMarkRect=GetMarkedRect();
        aDistortedRect=XPolygon(aMarkRect);
        Show();
        return true;
    }
    else
    {
        return false;
    }
}
 
void SdrDragDistort::MovAllPoints(basegfx::B2DPolyPolygon& rTarget)
{
    if (!bContortion)
        return;
 
    SdrPageView* pPV = getSdrDragView().GetSdrPageView();
 
    if(pPV && pPV->HasMarkedObjPageView())
    {
        basegfx::B2DPolyPolygon aDragPolygon(rTarget);
        const basegfx::B2DRange aOriginalRange = vcl::unotools::b2DRectangleFromRectangle(aMarkRect);
        const basegfx::B2DPoint aTopLeft(aDistortedRect[0].X(), aDistortedRect[0].Y());
        const basegfx::B2DPoint aTopRight(aDistortedRect[1].X(), aDistortedRect[1].Y());
        const basegfx::B2DPoint aBottomLeft(aDistortedRect[3].X(), aDistortedRect[3].Y());
        const basegfx::B2DPoint aBottomRight(aDistortedRect[2].X(), aDistortedRect[2].Y());
 
        rTarget = basegfx::utils::distort(aDragPolygon, aOriginalRange, aTopLeft, aTopRight, aBottomLeft, aBottomRight);
    }
}
 
void SdrDragDistort::MoveSdrDrag(const Point& rPnt)
{
    if (!DragStat().CheckMinMoved(rPnt))
        return;
 
    Point aPnt(GetSnapPos(rPnt));
 
    if (getSdrDragView().IsOrtho())
        OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
 
    bool bNewContortion=(bContortionAllowed && !getSdrDragView().IsCrookNoContortion()) || !bNoContortionAllowed;
 
    if (bNewContortion!=bContortion || aDistortedRect[nPolyPt]!=aPnt)
    {
        Hide();
        aDistortedRect[nPolyPt]=aPnt;
        bContortion=bNewContortion;
        DragStat().NextMove(aPnt);
        Show();
    }
}
 
bool SdrDragDistort::EndSdrDrag(bool bCopy)
{
    Hide();
    bool bDoDistort=DragStat().GetDX()!=0 || DragStat().GetDY()!=0;
 
    if (bDoDistort)
    {
        getSdrDragView().DistortMarkedObj(aMarkRect,aDistortedRect,!bContortion,bCopy);
        return true;
    }
 
    return false;
}
 
PointerStyle SdrDragDistort::GetSdrDragPointer() const
{
    return PointerStyle::RefHand;
}
 
void SdrDragDistort::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
{
    const bool bDoDistort(DragStat().GetDX()!=0 || DragStat().GetDY()!=0);
 
    if (bDoDistort)
    {
        SdrEditView::ImpDistortObj(&rTarget, aMarkRect, aDistortedRect, !bContortion);
    }
}
 
void SdrDragDistort::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget)
{
    // use helper derived from old stuff
    MovAllPoints(rTarget);
}
 
 
SdrDragCrop::SdrDragCrop(SdrDragView& rNewView)
:   SdrDragObjOwn(rNewView)
{
    // switch off solid dragging for crop; it just makes no sense since showing
    // a 50% transparent object above the original will not be visible
    setSolidDraggingActive(false);
}
 
OUString SdrDragCrop::GetSdrDragComment() const
{
    OUString aStr = ImpGetDescriptionStr(STR_DragMethCrop)
            + " (x="
            + getSdrDragView().GetModel().GetMetricString(DragStat().GetDX())
            + " y="
            + getSdrDragView().GetModel().GetMetricString(DragStat().GetDY())
            + ")";
 
    if(getSdrDragView().IsDragWithCopy())
        aStr += SvxResId(STR_EditWithCopy);
    return aStr;
}
 
bool SdrDragCrop::BeginSdrDrag()
{
    // call parent
    bool bRetval(SdrDragObjOwn::BeginSdrDrag());
 
    if(!GetDragHdl())
    {
        // we need the DragHdl, break if not there
        bRetval = false;
    }
 
    return bRetval;
}
 
bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
{
    Hide();
 
    if(0 == DragStat().GetDX() && 0 == DragStat().GetDY())
    {
        // no change, done
        return false;
    }
 
    const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
 
    if(1 != rMarkList.GetMarkCount())
    {
        // Crop only with single Object selected
        return false;
    }
 
    // 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();
    rtl::Reference<SdrObject> pFullDragClone;
    bool bExternal(false);
    SdrObject* pExternalSdrObject(nullptr);
 
    // 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 = pSdrObject->getFullDragClone();
 
        if(dynamic_cast< SdrGrafObj* >(pFullDragClone.get()))
        {
            bExternal = true;
            pExternalSdrObject = pSdrObject;
            pSdrObject = pFullDragClone.get();
        }
    }
 
    // get and check for SdrGrafObj now
    SdrGrafObj* pObj = dynamic_cast<SdrGrafObj*>( pSdrObject );
 
    if(!pObj)
    {
        return false;
    }
 
    // no undo for external needed, done there
    const bool bUndo(!bExternal && getSdrDragView().IsUndoEnabled());
 
    if(bUndo)
    {
        OUString aUndoStr = ImpGetDescriptionStr(STR_DragMethCrop);
 
        getSdrDragView().BegUndo( aUndoStr );
        getSdrDragView().AddUndo(getSdrDragView().GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
        // also need attr undo, the SdrGrafCropItem will be changed
        getSdrDragView().AddUndo(getSdrDragView().GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
    }
 
    // get the original objects transformation
    basegfx::B2DHomMatrix aOriginalMatrix;
    basegfx::B2DPolyPolygon aPolyPolygon;
    bool bShearCorrected(false);
    pObj->TRGetBaseGeometry(aOriginalMatrix, aPolyPolygon);
 
    {   // correct shear, it comes currently mirrored from TRGetBaseGeometry, can be removed with aw080
        const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aOriginalMatrix);
 
        if(!basegfx::fTools::equalZero(aTmpDecomp.getShearX()))
        {
            bShearCorrected = true;
            aOriginalMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
                aTmpDecomp.getScale(),
                -aTmpDecomp.getShearX(),
                aTmpDecomp.getRotate(),
                aTmpDecomp.getTranslate());
        }
    }
 
    // 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);
    bool bOnAxis(false);
 
    switch(GetDragHdlKind())
    {
        case SdrHdlKind::UpperLeft: aLocalStart.setX(1.0); aLocalStart.setY(1.0); break;
        case SdrHdlKind::Upper: aLocalStart.setX(0.5); aLocalStart.setY(1.0); bOnAxis = true; break;
        case SdrHdlKind::UpperRight: aLocalStart.setX(0.0); aLocalStart.setY(1.0); break;
        case SdrHdlKind::Left : aLocalStart.setX(1.0); aLocalStart.setY(0.5); bOnAxis = true; break;
        case SdrHdlKind::Right: aLocalStart.setX(0.0); aLocalStart.setY(0.5); bOnAxis = true; break;
        case SdrHdlKind::LowerLeft: aLocalStart.setX(1.0); aLocalStart.setY(0.0); break;
        case SdrHdlKind::Lower: aLocalStart.setX(0.5); aLocalStart.setY(0.0); bOnAxis = true; break;
        case SdrHdlKind::LowerRight: aLocalStart.setX(0.0); aLocalStart.setY(0.0); break;
        default: break;
    }
 
    // 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
    if(bOnAxis)
    {
        if(basegfx::fTools::equal(aLocalStart.getX(), 0.5))
        {
            aLocalCurrent.setX(aLocalStart.getX());
        }
        else
        {
            aLocalCurrent.setY(aLocalStart.getY());
        }
    }
 
    // create internal change in unit coordinates
    basegfx::B2DHomMatrix aDiscreteChangeMatrix;
 
    if(!basegfx::fTools::equal(aLocalCurrent.getX(), aLocalStart.getX()))
    {
        if(aLocalStart.getX() < 0.5)
        {
            aDiscreteChangeMatrix.scale(aLocalCurrent.getX(), 1.0);
        }
        else
        {
            aDiscreteChangeMatrix.scale(1.0 - aLocalCurrent.getX(), 1.0);
            aDiscreteChangeMatrix.translate(aLocalCurrent.getX(), 0.0);
        }
    }
 
    if(!basegfx::fTools::equal(aLocalCurrent.getY(), aLocalStart.getY()))
    {
        if(aLocalStart.getY() < 0.5)
        {
            aDiscreteChangeMatrix.scale(1.0, aLocalCurrent.getY());
        }
        else
        {
            aDiscreteChangeMatrix.scale(1.0, 1.0 - aLocalCurrent.getY());
            aDiscreteChangeMatrix.translate(0.0, aLocalCurrent.getY());
        }
    }
 
    // 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
    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(aOriginalMatrixDecomp.getScale()),
            aOriginalMatrixDecomp.getTranslate()));
 
    // 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 aLocalStart point (opposed to dragged point), X scale and Y scale,
        // we call crop (virtual method) on pSdrObject which calls VirtFlyDrawObj
        // crop. Use aLocalStart unchanged, so being relative to the Crop-Action,
        // the called instance knows best how to use it
        const double fScaleX(aRangeNewNoShearNoRotate.getWidth() / aRangeOriginalNoShearNoRotate.getWidth());
        const double fScaleY(aRangeNewNoShearNoRotate.getHeight() / aRangeOriginalNoShearNoRotate.getHeight());
 
        pExternalSdrObject->Crop(
            aLocalStart,
            fScaleX,
            fScaleY);
    }
    else
    {
        // prepare matrix to apply to object; evtl. back-correct shear
        basegfx::B2DHomMatrix aNewObjectMatrix(aOriginalMatrix * aDiscreteChangeMatrix);
 
        if(bShearCorrected)
        {
            // back-correct shear
            const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aNewObjectMatrix);
 
            aNewObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
                aTmpDecomp.getScale(),
                -aTmpDecomp.getShearX(),
                aTmpDecomp.getRotate(),
                aTmpDecomp.getTranslate());
        }
 
        // 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<tools::Long>(aRangeOriginalNoShearNoRotate.getMinX()),
            basegfx::fround<tools::Long>(aRangeOriginalNoShearNoRotate.getMinY()),
            basegfx::fround<tools::Long>(aRangeOriginalNoShearNoRotate.getMaxX()),
            basegfx::fround<tools::Long>(aRangeOriginalNoShearNoRotate.getMaxY()));
        tools::Rectangle aNewRect(
            basegfx::fround<tools::Long>(aRangeNewNoShearNoRotate.getMinX()),
            basegfx::fround<tools::Long>(aRangeNewNoShearNoRotate.getMinY()),
            basegfx::fround<tools::Long>(aRangeNewNoShearNoRotate.getMaxX()),
            basegfx::fround<tools::Long>(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());
        // tdf#117145 Usually Writer will go the bExternal path (see above), but more correct for
        // the future is to use the MapMode from the SdrModel/SfxItemPool if the Writer's current
        // special handling should be unified to this path in the future. Usually it *should* be
        // MapUnit::Map100thMM, but better do not mix up Units.
        // Checked now what SwVirtFlyDrawObj::NbcCrop is doing - it calculates everything forced
        // to MapUnit::Map100thMM, but extracts/packs Twips to the used SdrGrafCropItem in Writer.
        const MapMode aMapModePool(pObj->getSdrModelFromSdrObject().GetItemPool().GetMetric(0));
        Size aGraphicSize(rGraphicObject.GetPrefSize());
 
        if(MapUnit::MapPixel == rGraphicObject.GetPrefMapMode().GetMapUnit())
        {
            aGraphicSize = Application::GetDefaultDevice()->PixelToLogic(aGraphicSize, aMapModePool);
        }
        else
        {
            aGraphicSize = OutputDevice::LogicToLogic(aGraphicSize, rGraphicObject.GetPrefMapMode(), aMapModePool);
        }
 
        if(0 == aGraphicSize.Width() || 0 == aGraphicSize.Height())
        {
            return false;
        }
 
        const SdrGrafCropItem& rOldCrop = pObj->GetMergedItem(SDRATTR_GRAFCROP);
        double fScaleX = ( aGraphicSize.Width() - rOldCrop.GetLeft() - rOldCrop.GetRight() ) / static_cast<double>(aOldRect.GetWidth());
        double fScaleY = ( aGraphicSize.Height() - rOldCrop.GetTop() - rOldCrop.GetBottom() ) / static_cast<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();
        SfxItemSetFixed<SDRATTR_GRAFCROP, SDRATTR_GRAFCROP> aSet( rPool );
        aSet.Put( SdrGrafCropItem( nLeftCrop, nTopCrop, nRightCrop, nBottomCrop ) );
        getSdrDragView().SetAttributes( aSet, false );
    }
 
    if(bUndo)
    {
        getSdrDragView().EndUndo();
    }
 
    return true;
}
 
PointerStyle SdrDragCrop::GetSdrDragPointer() const
{
    return PointerStyle::Crop;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'Intersection' is required to be utilized.

V530 The return value of function 'Intersection' is required to be utilized.

V530 The return value of function 'Union' is required to be utilized.