/* -*- 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 <algorithm>
#include <cassert>

#include <svx/svdhdl.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svdmrkv.hxx>
#include <utility>
#include <vcl/settings.hxx>
#include <vcl/virdev.hxx>
#include <vcl/ptrstyle.hxx>

#include <svx/sxekitm.hxx>
#include <svx/strings.hrc>
#include <svx/svdmodel.hxx>
#include "gradtrns.hxx"
#include <svx/xflgrit.hxx>
#include <svx/svdundo.hxx>
#include <svx/dialmgr.hxx>
#include <svx/xflftrit.hxx>

#include <svx/svdopath.hxx>
#include <basegfx/vector/b2dvector.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <svx/sdr/overlay/overlaymanager.hxx>
#include <svx/sdr/overlay/overlayanimatedbitmapex.hxx>
#include <svx/sdr/overlay/overlaybitmapex.hxx>
#include <sdr/overlay/overlayline.hxx>
#include <sdr/overlay/overlaytriangle.hxx>
#include <sdr/overlay/overlayhandle.hxx>
#include <sdr/overlay/overlayrectangle.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <vcl/svapp.hxx>
#include <svx/sdr/overlay/overlaypolypolygon.hxx>
#include <tools/lazydelete.hxx>
#include <vcl/BitmapTools.hxx>
#include <svx/sdr/contact/objectcontact.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <osl/diagnose.h>

#include <basegfx/polygon/b2dpolygontools.hxx>
#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <memory>
#include <bitmaps.hlst>

namespace {

// #i15222#
// Due to the resource problems in Win95/98 with bitmap resources I
// will change this handle bitmap providing class. Old version was splitting
// and preparing all small handle bitmaps in device bitmap format, now this will
// be done on the fly. Thus, there is only one big bitmap in memory. With
// three source bitmaps, this will be 3 system bitmap resources instead of hundreds.
// The price for that needs to be evaluated. Maybe we will need another change here
// if this is too expensive.
class SdrHdlBitmapSet
{
    // the bitmap holding all information
    Bitmap                      maMarkersBitmap;

    // the cropped Bitmaps for reusage
    ::std::vector< Bitmap >     maRealMarkers;

    // helpers
    Bitmap& impGetOrCreateTargetBitmap(sal_uInt16 nIndex, const tools::Rectangle& rRectangle);

public:
    explicit SdrHdlBitmapSet();

    const Bitmap& GetBitmap(BitmapMarkerKind eKindOfMarker, sal_uInt16 nInd);
};

}

#define KIND_COUNT          (14)
#define INDEX_COUNT         (6)
#define INDIVIDUAL_COUNT    (5)

SdrHdlBitmapSet::SdrHdlBitmapSet()
    :   maMarkersBitmap(SIP_SA_MARKERS),
        // 15 kinds (BitmapMarkerKind) use index [0..5] + 5 extra
        maRealMarkers((KIND_COUNT * INDEX_COUNT) + INDIVIDUAL_COUNT)
{
}

Bitmap& SdrHdlBitmapSet::impGetOrCreateTargetBitmap(sal_uInt16 nIndex, const tools::Rectangle& rRectangle)
{
    Bitmap& rTargetBitmap = maRealMarkers[nIndex];

    if(rTargetBitmap.IsEmpty())
    {
        rTargetBitmap = maMarkersBitmap;
        rTargetBitmap.Crop(rRectangle);
    }

    return rTargetBitmap;
}

// change getting of bitmap to use the big resource bitmap
const Bitmap& SdrHdlBitmapSet::GetBitmap(BitmapMarkerKind eKindOfMarker, sal_uInt16 nInd)
{
    // fill in size and source position in maMarkersBitmap
    const sal_uInt16 nYPos(nInd * 11);

    switch(eKindOfMarker)
    {
        default:
        {
            OSL_FAIL( "Unknown kind of marker." );
            [[fallthrough]]; // return Rect_9x9 as default
        }
        case BitmapMarkerKind::Rect_9x9:
        {
            return impGetOrCreateTargetBitmap((1 * INDEX_COUNT) + nInd, tools::Rectangle(Point(7, nYPos), Size(9, 9)));
        }

        case BitmapMarkerKind::Rect_7x7:
        {
            return impGetOrCreateTargetBitmap((0 * INDEX_COUNT) + nInd, tools::Rectangle(Point(0, nYPos), Size(7, 7)));
        }

        case BitmapMarkerKind::Rect_11x11:
        {
            return impGetOrCreateTargetBitmap((2 * INDEX_COUNT) + nInd, tools::Rectangle(Point(16, nYPos), Size(11, 11)));
        }

        case BitmapMarkerKind::Rect_13x13:
        {
            const sal_uInt16 nIndex((3 * INDEX_COUNT) + nInd);

            switch(nInd)
            {
                case 0:
                {
                    return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(72, 66), Size(13, 13)));
                }
                case 1:
                {
                    return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(85, 66), Size(13, 13)));
                }
                case 2:
                {
                    return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(72, 79), Size(13, 13)));
                }
                case 3:
                {
                    return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(85, 79), Size(13, 13)));
                }
                case 4:
                {
                    return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(98, 79), Size(13, 13)));
                }
                default: // case 5:
                {
                    return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(98, 66), Size(13, 13)));
                }
            }
        }

        case BitmapMarkerKind::Circ_7x7:
        case BitmapMarkerKind::Customshape_7x7:
        {
            return impGetOrCreateTargetBitmap((4 * INDEX_COUNT) + nInd, tools::Rectangle(Point(27, nYPos), Size(7, 7)));
        }

        case BitmapMarkerKind::Circ_9x9:
        case BitmapMarkerKind::Customshape_9x9:
        {
            return impGetOrCreateTargetBitmap((5 * INDEX_COUNT) + nInd, tools::Rectangle(Point(34, nYPos), Size(9, 9)));
        }

        case BitmapMarkerKind::Circ_11x11:
        case BitmapMarkerKind::Customshape_11x11:
        {
            return impGetOrCreateTargetBitmap((6 * INDEX_COUNT) + nInd, tools::Rectangle(Point(43, nYPos), Size(11, 11)));
        }

        case BitmapMarkerKind::Elli_7x9:
        {
            return impGetOrCreateTargetBitmap((7 * INDEX_COUNT) + nInd, tools::Rectangle(Point(54, nYPos), Size(7, 9)));
        }

        case BitmapMarkerKind::Elli_9x11:
        {
            return impGetOrCreateTargetBitmap((8 * INDEX_COUNT) + nInd, tools::Rectangle(Point(61, nYPos), Size(9, 11)));
        }

        case BitmapMarkerKind::Elli_9x7:
        {
            return impGetOrCreateTargetBitmap((9 * INDEX_COUNT) + nInd, tools::Rectangle(Point(70, nYPos), Size(9, 7)));
        }

        case BitmapMarkerKind::Elli_11x9:
        {
            return impGetOrCreateTargetBitmap((10 * INDEX_COUNT) + nInd, tools::Rectangle(Point(79, nYPos), Size(11, 9)));
        }

        case BitmapMarkerKind::RectPlus_7x7:
        {
            return impGetOrCreateTargetBitmap((11 * INDEX_COUNT) + nInd, tools::Rectangle(Point(90, nYPos), Size(7, 7)));
        }

        case BitmapMarkerKind::RectPlus_9x9:
        {
            return impGetOrCreateTargetBitmap((12 * INDEX_COUNT) + nInd, tools::Rectangle(Point(97, nYPos), Size(9, 9)));
        }

        case BitmapMarkerKind::RectPlus_11x11:
        {
            return impGetOrCreateTargetBitmap((13 * INDEX_COUNT) + nInd, tools::Rectangle(Point(106, nYPos), Size(11, 11)));
        }

        case BitmapMarkerKind::Crosshair:
        {
            return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 0, tools::Rectangle(Point(0, 68), Size(15, 15)));
        }

        case BitmapMarkerKind::Glue:
        {
            return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 1, tools::Rectangle(Point(15, 76), Size(9, 9)));
        }

        case BitmapMarkerKind::Glue_Deselected:
        {
            return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 2, tools::Rectangle(Point(15, 67), Size(9, 9)));
        }

        case BitmapMarkerKind::Anchor: // AnchorTR for SW
        case BitmapMarkerKind::AnchorTR:
        {
            return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 3, tools::Rectangle(Point(24, 67), Size(24, 24)));
        }

        // add AnchorPressed to be able to animate anchor control
        case BitmapMarkerKind::AnchorPressed:
        case BitmapMarkerKind::AnchorPressedTR:
        {
            return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 4, tools::Rectangle(Point(48, 67), Size(24, 24)));
        }
    }
}


SdrHdl::SdrHdl():
    m_pObj(nullptr),
    m_pPV(nullptr),
    m_pHdlList(nullptr),
    m_eKind(SdrHdlKind::Move),
    m_nRotationAngle(0),
    m_nObjHdlNum(0),
    m_nPolyNum(0),
    m_nPPntNum(0),
    m_nSourceHdlNum(0),
    m_bSelect(false),
    m_b1PixMore(false),
    m_bPlusHdl(false),
    mbMoveOutside(false),
    mbMouseOver(false)
{
}

SdrHdl::SdrHdl(const Point& rPnt, SdrHdlKind eNewKind):
    m_pObj(nullptr),
    m_pPV(nullptr),
    m_pHdlList(nullptr),
    m_aPos(rPnt),
    m_eKind(eNewKind),
    m_nRotationAngle(0),
    m_nObjHdlNum(0),
    m_nPolyNum(0),
    m_nPPntNum(0),
    m_nSourceHdlNum(0),
    m_bSelect(false),
    m_b1PixMore(false),
    m_bPlusHdl(false),
    mbMoveOutside(false),
    mbMouseOver(false)
{
}

SdrHdl::~SdrHdl()
{
    GetRidOfIAObject();
}

void SdrHdl::Set1PixMore(bool bJa)
{
    if(m_b1PixMore != bJa)
    {
        m_b1PixMore = bJa;

        // create new display
        Touch();
    }
}

void SdrHdl::SetMoveOutside( bool bMoveOutside )
{
    if(mbMoveOutside != bMoveOutside)
    {
        mbMoveOutside = bMoveOutside;

        // create new display
        Touch();
    }
}

void SdrHdl::SetRotationAngle(Degree100 n)
{
    if(m_nRotationAngle != n)
    {
        m_nRotationAngle = n;

        // create new display
        Touch();
    }
}

void SdrHdl::SetPos(const Point& rPnt)
{
    if(m_aPos != rPnt)
    {
        // remember new position
        m_aPos = rPnt;

        // create new display
        Touch();
    }
}

void SdrHdl::SetSelected(bool bJa)
{
    if(m_bSelect != bJa)
    {
        // remember new value
        m_bSelect = bJa;

        // create new display
        Touch();
    }
}

void SdrHdl::SetHdlList(SdrHdlList* pList)
{
    if(m_pHdlList != pList)
    {
        // remember list
        m_pHdlList = pList;

        // now it's possible to create graphic representation
        Touch();
    }
}

void SdrHdl::SetObj(SdrObject* pNewObj)
{
    if(m_pObj != pNewObj)
    {
        // remember new object
        m_pObj = pNewObj;

        // graphic representation may have changed
        Touch();
    }
}

void SdrHdl::Touch()
{
    // force update of graphic representation
    CreateB2dIAObject();
}

void SdrHdl::GetRidOfIAObject()
{

    // OVERLAYMANAGER
    maOverlayGroup.clear();
}

void SdrHdl::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();

    if(!m_pHdlList || !m_pHdlList->GetView() || m_pHdlList->GetView()->areMarkHandlesHidden())
        return;

    BitmapColorIndex eColIndex = BitmapColorIndex::LightGreen;
    BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_7x7;

    bool bRot = m_pHdlList->IsRotateShear();
    if(m_pObj)
        eColIndex = m_bSelect ? BitmapColorIndex::Cyan : BitmapColorIndex::LightCyan;
    if(bRot)
    {
        // red rotation handles
        if(m_pObj && m_bSelect)
            eColIndex = BitmapColorIndex::Red;
        else
            eColIndex = BitmapColorIndex::LightRed;
    }

    switch(m_eKind)
    {
        case SdrHdlKind::Move:
        {
            eKindOfMarker = m_b1PixMore ? BitmapMarkerKind::Rect_9x9 : BitmapMarkerKind::Rect_7x7;
            break;
        }
        case SdrHdlKind::UpperLeft:
        case SdrHdlKind::UpperRight:
        case SdrHdlKind::LowerLeft:
        case SdrHdlKind::LowerRight:
        {
            // corner handles
            if(bRot)
            {
                eKindOfMarker = BitmapMarkerKind::Circ_7x7;
            }
            else
            {
                eKindOfMarker = BitmapMarkerKind::Rect_7x7;
            }
            break;
        }
        case SdrHdlKind::Upper:
        case SdrHdlKind::Lower:
        {
            // Upper/Lower handles
            if(bRot)
            {
                eKindOfMarker = BitmapMarkerKind::Elli_9x7;
            }
            else
            {
                eKindOfMarker = BitmapMarkerKind::Rect_7x7;
            }
            break;
        }
        case SdrHdlKind::Left:
        case SdrHdlKind::Right:
        {
            // Left/Right handles
            if(bRot)
            {
                eKindOfMarker = BitmapMarkerKind::Elli_7x9;
            }
            else
            {
                eKindOfMarker = BitmapMarkerKind::Rect_7x7;
            }
            break;
        }
        case SdrHdlKind::Poly:
        {
            if(bRot)
            {
                eKindOfMarker = m_b1PixMore ? BitmapMarkerKind::Circ_9x9 : BitmapMarkerKind::Circ_7x7;
            }
            else
            {
                eKindOfMarker = m_b1PixMore ? BitmapMarkerKind::Rect_9x9 : BitmapMarkerKind::Rect_7x7;
            }
            break;
        }
        case SdrHdlKind::BezierWeight: // weight at poly
        {
            eKindOfMarker = BitmapMarkerKind::Circ_7x7;
            break;
        }
        case SdrHdlKind::Circle:
        {
            eKindOfMarker = BitmapMarkerKind::Rect_11x11;
            break;
        }
        case SdrHdlKind::Ref1:
        case SdrHdlKind::Ref2:
        {
            eKindOfMarker = BitmapMarkerKind::Crosshair;
            break;
        }
        case SdrHdlKind::Glue:
        {
            eKindOfMarker = BitmapMarkerKind::Glue;
            break;
        }
        case SdrHdlKind::Anchor:
        {
            eKindOfMarker = BitmapMarkerKind::Anchor;
            break;
        }
        case SdrHdlKind::User:
        {
            break;
        }
        // top right anchor for SW
        case SdrHdlKind::Anchor_TR:
        {
            eKindOfMarker = BitmapMarkerKind::AnchorTR;
            break;
        }

        // for SJ and the CustomShapeHandles:
        case SdrHdlKind::CustomShape1:
        {
            eKindOfMarker = m_b1PixMore ? BitmapMarkerKind::Customshape_9x9 : BitmapMarkerKind::Customshape_7x7;
            eColIndex = BitmapColorIndex::Yellow;
            break;
        }
        default:
            break;
    }

    SdrMarkView* pView = m_pHdlList->GetView();
    SdrPageView* pPageView = pView->GetSdrPageView();

    if(!pPageView)
        return;

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b];
        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            Point aMoveOutsideOffset(0, 0);
            OutputDevice& rOutDev = rPageWindow.GetPaintWindow().GetOutputDevice();

            // add offset if necessary
            if(m_pHdlList->IsMoveOutside() || mbMoveOutside)
            {
                Size aOffset = rOutDev.PixelToLogic(Size(4, 4));

                if(m_eKind == SdrHdlKind::UpperLeft || m_eKind == SdrHdlKind::Upper || m_eKind == SdrHdlKind::UpperRight)
                    aMoveOutsideOffset.AdjustY( -(aOffset.Width()) );
                if(m_eKind == SdrHdlKind::LowerLeft || m_eKind == SdrHdlKind::Lower || m_eKind == SdrHdlKind::LowerRight)
                    aMoveOutsideOffset.AdjustY(aOffset.Height() );
                if(m_eKind == SdrHdlKind::UpperLeft || m_eKind == SdrHdlKind::Left  || m_eKind == SdrHdlKind::LowerLeft)
                    aMoveOutsideOffset.AdjustX( -(aOffset.Width()) );
                if(m_eKind == SdrHdlKind::UpperRight || m_eKind == SdrHdlKind::Right || m_eKind == SdrHdlKind::LowerRight)
                    aMoveOutsideOffset.AdjustX(aOffset.Height() );
            }

            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if (xManager.is())
            {
                basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y());
                std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject;
                if (getenv ("SVX_DRAW_HANDLES") && (eKindOfMarker == BitmapMarkerKind::Rect_7x7 || eKindOfMarker == BitmapMarkerKind::Rect_9x9 || eKindOfMarker == BitmapMarkerKind::Rect_11x11))
                {
                    double fSize = 7.0;
                    switch (eKindOfMarker)
                    {
                        case BitmapMarkerKind::Rect_9x9:
                            fSize = 9.0;
                            break;
                        case BitmapMarkerKind::Rect_11x11:
                            fSize = 11.0;
                            break;
                        default:
                            break;
                    }
                    float fScalingFactor = rOutDev.GetDPIScaleFactor();
                    basegfx::B2DSize aB2DSize(fSize * fScalingFactor, fSize * fScalingFactor);

                    Color aHandleFillColor(COL_LIGHTGREEN);
                    switch (eColIndex)
                    {
                        case BitmapColorIndex::Cyan:
                            aHandleFillColor = COL_CYAN;
                            break;
                        case BitmapColorIndex::LightCyan:
                            aHandleFillColor = COL_LIGHTCYAN;
                            break;
                        case BitmapColorIndex::Red:
                            aHandleFillColor = COL_RED;
                            break;
                        case BitmapColorIndex::LightRed:
                            aHandleFillColor = COL_LIGHTRED;
                            break;
                        case BitmapColorIndex::Yellow:
                            aHandleFillColor = COL_YELLOW;
                            break;
                        default:
                            break;
                    }
                    pNewOverlayObject.reset(new sdr::overlay::OverlayHandle(aPosition, aB2DSize, /*HandleStrokeColor*/COL_BLACK, aHandleFillColor));
                }
                else
                {
                    pNewOverlayObject = CreateOverlayObject(
                                            aPosition, eColIndex, eKindOfMarker,
                                            aMoveOutsideOffset);
                }

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pNewOverlayObject),
                    rPageWindow.GetObjectContact(),
                    *xManager);
            }
        }
    }
}

BitmapMarkerKind SdrHdl::GetNextBigger(BitmapMarkerKind eKnd)
{
    BitmapMarkerKind eRetval(eKnd);

    switch(eKnd)
    {
        case BitmapMarkerKind::Rect_7x7:          eRetval = BitmapMarkerKind::Rect_9x9;         break;
        case BitmapMarkerKind::Rect_9x9:          eRetval = BitmapMarkerKind::Rect_11x11;       break;
        case BitmapMarkerKind::Rect_11x11:        eRetval = BitmapMarkerKind::Rect_13x13;       break;

        case BitmapMarkerKind::Circ_7x7:          eRetval = BitmapMarkerKind::Circ_9x9;         break;
        case BitmapMarkerKind::Circ_9x9:          eRetval = BitmapMarkerKind::Circ_11x11;       break;

        case BitmapMarkerKind::Customshape_7x7:       eRetval = BitmapMarkerKind::Customshape_9x9;      break;
        case BitmapMarkerKind::Customshape_9x9:       eRetval = BitmapMarkerKind::Customshape_11x11;    break;
        //case BitmapMarkerKind::Customshape_11x11:   eRetval = ; break;

        case BitmapMarkerKind::Elli_7x9:          eRetval = BitmapMarkerKind::Elli_9x11;        break;

        case BitmapMarkerKind::Elli_9x7:          eRetval = BitmapMarkerKind::Elli_11x9;        break;

        case BitmapMarkerKind::RectPlus_7x7:      eRetval = BitmapMarkerKind::RectPlus_9x9;     break;
        case BitmapMarkerKind::RectPlus_9x9:      eRetval = BitmapMarkerKind::RectPlus_11x11;   break;

        // let anchor blink with its pressed state
        case BitmapMarkerKind::Anchor:            eRetval = BitmapMarkerKind::AnchorPressed;    break;

        // same for AnchorTR
        case BitmapMarkerKind::AnchorTR:          eRetval = BitmapMarkerKind::AnchorPressedTR;  break;
        default:
            break;
    }

    return eRetval;
}

namespace
{

OUString appendMarkerName(BitmapMarkerKind eKindOfMarker)
{
    switch(eKindOfMarker)
    {
        case BitmapMarkerKind::Rect_7x7:
            return u"rect7"_ustr;
        case BitmapMarkerKind::Rect_9x9:
            return u"rect9"_ustr;
        case BitmapMarkerKind::Rect_11x11:
            return u"rect11"_ustr;
        case BitmapMarkerKind::Rect_13x13:
            return u"rect13"_ustr;
        case BitmapMarkerKind::Circ_7x7:
        case BitmapMarkerKind::Customshape_7x7:
            return u"circ7"_ustr;
        case BitmapMarkerKind::Circ_9x9:
        case BitmapMarkerKind::Customshape_9x9:
            return u"circ9"_ustr;
        case BitmapMarkerKind::Circ_11x11:
        case BitmapMarkerKind::Customshape_11x11:
            return u"circ11"_ustr;
        case BitmapMarkerKind::Elli_7x9:
            return u"elli7x9"_ustr;
        case BitmapMarkerKind::Elli_9x11:
            return u"elli9x11"_ustr;
        case BitmapMarkerKind::Elli_9x7:
            return u"elli9x7"_ustr;
        case BitmapMarkerKind::Elli_11x9:
            return u"elli11x9"_ustr;
        case BitmapMarkerKind::RectPlus_7x7:
            return u"rectplus7"_ustr;
        case BitmapMarkerKind::RectPlus_9x9:
            return u"rectplus9"_ustr;
        case BitmapMarkerKind::RectPlus_11x11:
            return u"rectplus11"_ustr;
        case BitmapMarkerKind::Crosshair:
            return u"cross"_ustr;
        case BitmapMarkerKind::Anchor:
        case BitmapMarkerKind::AnchorTR:
            return u"anchor"_ustr;
        case BitmapMarkerKind::AnchorPressed:
        case BitmapMarkerKind::AnchorPressedTR:
            return u"anchor-pressed"_ustr;
        case BitmapMarkerKind::Glue:
            return u"glue-selected"_ustr;
        case BitmapMarkerKind::Glue_Deselected:
            return u"glue-unselected"_ustr;
        default:
            break;
    }
    return OUString();
}

OUString appendMarkerColor(BitmapColorIndex eIndex)
{
    switch(eIndex)
    {
        case BitmapColorIndex::LightGreen:
            return u"1"_ustr;
        case BitmapColorIndex::Cyan:
            return u"2"_ustr;
        case BitmapColorIndex::LightCyan:
            return u"3"_ustr;
        case BitmapColorIndex::Red:
            return u"4"_ustr;
        case BitmapColorIndex::LightRed:
            return u"5"_ustr;
        case BitmapColorIndex::Yellow:
            return u"6"_ustr;
        default:
            break;
    }
    return OUString();
}

Bitmap ImpGetBitmap(BitmapMarkerKind eKindOfMarker, BitmapColorIndex eIndex)
{
    // use this code path only when we use HiDPI (for now)
    if (Application::GetDefaultDevice()->GetDPIScalePercentage() > 100)
    {
        OUString sMarkerName = appendMarkerName(eKindOfMarker);
        if (!sMarkerName.isEmpty())
        {
            OUString sMarkerPrefix(u"svx/res/marker-"_ustr);
            Bitmap aBitmap;

            if (eKindOfMarker == BitmapMarkerKind::Crosshair
             || eKindOfMarker == BitmapMarkerKind::Anchor
             || eKindOfMarker == BitmapMarkerKind::AnchorTR
             || eKindOfMarker == BitmapMarkerKind::AnchorPressed
             || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR
             || eKindOfMarker == BitmapMarkerKind::Glue
             || eKindOfMarker == BitmapMarkerKind::Glue_Deselected)
            {
                aBitmap = vcl::bitmap::loadFromName(sMarkerPrefix + sMarkerName + ".png");
            }
            else
            {
                aBitmap = vcl::bitmap::loadFromName(sMarkerPrefix + sMarkerName + "-" + appendMarkerColor(eIndex) + ".png");
            }

            if (!aBitmap.IsEmpty())
                return aBitmap;
        }
    }

    // if we can't load the marker...

    static tools::DeleteOnDeinit< SdrHdlBitmapSet > aModernSet {};
    return aModernSet.get()->GetBitmap(eKindOfMarker, sal_uInt16(eIndex));
}

} // end anonymous namespace

std::unique_ptr<sdr::overlay::OverlayObject> SdrHdl::CreateOverlayObject(
    const basegfx::B2DPoint& rPos,
    BitmapColorIndex eColIndex, BitmapMarkerKind eKindOfMarker, Point aMoveOutsideOffset)
{
    std::unique_ptr<sdr::overlay::OverlayObject> pRetval;

    // support bigger sizes
    bool bForceBiggerSize(false);

    if (m_pHdlList && m_pHdlList->GetHdlSize() > 3)
    {
        switch(eKindOfMarker)
        {
            case BitmapMarkerKind::Anchor:
            case BitmapMarkerKind::AnchorPressed:
            case BitmapMarkerKind::AnchorTR:
            case BitmapMarkerKind::AnchorPressedTR:
            {
                // #i121463# For anchor, do not simply make bigger because of HdlSize,
                // do it dependent of IsSelected() which Writer can set in drag mode
                if(IsSelected())
                {
                    bForceBiggerSize = true;
                }
                break;
            }
            default:
            {
                bForceBiggerSize = true;
                break;
            }
        }
    }

    if(bForceBiggerSize)
    {
        eKindOfMarker = GetNextBigger(eKindOfMarker);
    }

    // This handle has the focus, visualize it
    if(IsFocusHdl() && m_pHdlList && m_pHdlList->GetFocusHdl() == this)
    {
        // create animated handle
        BitmapMarkerKind eNextBigger = GetNextBigger(eKindOfMarker);

        if(eNextBigger == eKindOfMarker)
        {
            // this may happen for the not supported getting-bigger types.
            // Choose an alternative here
            switch(eKindOfMarker)
            {
                case BitmapMarkerKind::Rect_13x13:        eNextBigger = BitmapMarkerKind::Rect_11x11;   break;
                case BitmapMarkerKind::Circ_11x11:        eNextBigger = BitmapMarkerKind::Elli_11x9;    break;
                case BitmapMarkerKind::Elli_9x11:         eNextBigger = BitmapMarkerKind::Elli_11x9;    break;
                case BitmapMarkerKind::Elli_11x9:         eNextBigger = BitmapMarkerKind::Elli_9x11;    break;
                case BitmapMarkerKind::RectPlus_11x11:    eNextBigger = BitmapMarkerKind::Rect_13x13;   break;
                case BitmapMarkerKind::Glue:
                    eNextBigger = BitmapMarkerKind::Crosshair;
                    break;
                case BitmapMarkerKind::Crosshair:
                case BitmapMarkerKind::Glue_Deselected:
                    eNextBigger = BitmapMarkerKind::Glue;
                    break;
                default:
                    break;
            }
        }

        // create animated handle
        Bitmap aBmp1 = ImpGetBitmap(eKindOfMarker, eColIndex);
        Bitmap aBmp2 = ImpGetBitmap(eNextBigger,   eColIndex);

        // #i53216# Use system cursor blink time. Use the unsigned value.
        const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
        const sal_uInt64 nBlinkTime(rStyleSettings.GetCursorBlinkTime());

        if(eKindOfMarker == BitmapMarkerKind::Anchor || eKindOfMarker == BitmapMarkerKind::AnchorPressed)
        {
            // when anchor is used take upper left as reference point inside the handle
            pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmp1, aBmp2, nBlinkTime));
        }
        else if(eKindOfMarker == BitmapMarkerKind::AnchorTR || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR)
        {
            // AnchorTR for SW, take top right as (0,0)
            pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmp1, aBmp2, nBlinkTime,
                static_cast<sal_uInt16>(aBmp1.GetSizePixel().Width() - 1), 0,
                static_cast<sal_uInt16>(aBmp2.GetSizePixel().Width() - 1), 0));
        }
        else
        {
            // create centered handle as default
            pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmp1, aBmp2, nBlinkTime,
                static_cast<sal_uInt16>(aBmp1.GetSizePixel().Width() - 1) >> 1,
                static_cast<sal_uInt16>(aBmp1.GetSizePixel().Height() - 1) >> 1,
                static_cast<sal_uInt16>(aBmp2.GetSizePixel().Width() - 1) >> 1,
                static_cast<sal_uInt16>(aBmp2.GetSizePixel().Height() - 1) >> 1));
        }
    }
    else
    {
        // create normal handle: use ImpGetBitmapEx(...) now
        Bitmap aBmp(ImpGetBitmap(eKindOfMarker, eColIndex));

        // When the image with handles is not found, the bitmap returned is
        // empty. This is a problem when we use LibreOffice as a library
        // (through LOKit - for example on Android) even when we don't show
        // the handles, because the hit test would always return false.
        //
        // This HACK replaces the empty bitmap with a black 13x13 bitmap handle
        // so that the hit test works for this case.
        if (aBmp.IsEmpty())
        {
            aBmp = Bitmap(Size(13, 13), vcl::PixelFormat::N24_BPP);
            aBmp.Erase(COL_BLACK);
        }

        if(eKindOfMarker == BitmapMarkerKind::Anchor || eKindOfMarker == BitmapMarkerKind::AnchorPressed)
        {
            // upper left as reference point inside the handle for AnchorPressed, too
            pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmp));
        }
        else if(eKindOfMarker == BitmapMarkerKind::AnchorTR || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR)
        {
            // AnchorTR for SW, take top right as (0,0)
            pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmp,
                static_cast<sal_uInt16>(aBmp.GetSizePixel().Width() - 1), 0));
        }
        else
        {
            sal_uInt16 nCenX(static_cast<sal_uInt16>(aBmp.GetSizePixel().Width() - 1) >> 1);
            sal_uInt16 nCenY(static_cast<sal_uInt16>(aBmp.GetSizePixel().Height() - 1) >> 1);

            if(aMoveOutsideOffset.X() > 0)
            {
                nCenX = 0;
            }
            else if(aMoveOutsideOffset.X() < 0)
            {
                nCenX = static_cast<sal_uInt16>(aBmp.GetSizePixel().Width() - 1);
            }

            if(aMoveOutsideOffset.Y() > 0)
            {
                nCenY = 0;
            }
            else if(aMoveOutsideOffset.Y() < 0)
            {
                nCenY = static_cast<sal_uInt16>(aBmp.GetSizePixel().Height() - 1);
            }

            // create centered handle as default
            pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmp, nCenX, nCenY));
        }
    }

    return pRetval;
}

bool SdrHdl::IsHdlHit(const Point& rPnt) const
{
    // OVERLAYMANAGER
    basegfx::B2DPoint aPosition(rPnt.X(), rPnt.Y());
    return maOverlayGroup.isHitLogic(aPosition);
}

PointerStyle SdrHdl::GetPointer() const
{
    PointerStyle ePtr=PointerStyle::Move;
    const bool bSize=m_eKind>=SdrHdlKind::UpperLeft && m_eKind<=SdrHdlKind::LowerRight;
    const bool bRot=m_pHdlList!=nullptr && m_pHdlList->IsRotateShear();
    const bool bDis=m_pHdlList!=nullptr && m_pHdlList->IsDistortShear();
    if (bSize && m_pHdlList!=nullptr && (bRot || bDis)) {
        switch (m_eKind) {
            case SdrHdlKind::UpperLeft: case SdrHdlKind::UpperRight:
            case SdrHdlKind::LowerLeft: case SdrHdlKind::LowerRight: ePtr=bRot ? PointerStyle::Rotate : PointerStyle::RefHand; break;
            case SdrHdlKind::Left : case SdrHdlKind::Right: ePtr=PointerStyle::VShear; break;
            case SdrHdlKind::Upper: case SdrHdlKind::Lower: ePtr=PointerStyle::HShear; break;
            default:
                break;
        }
    } else {
        // When resizing rotated rectangles, rotate the mouse cursor slightly, too
        if (bSize && m_nRotationAngle!=0_deg100) {
            Degree100 nHdlAngle(0);
            switch (m_eKind) {
                case SdrHdlKind::LowerRight: nHdlAngle=31500_deg100; break;
                case SdrHdlKind::Lower: nHdlAngle=27000_deg100; break;
                case SdrHdlKind::LowerLeft: nHdlAngle=22500_deg100; break;
                case SdrHdlKind::Left : nHdlAngle=18000_deg100; break;
                case SdrHdlKind::UpperLeft: nHdlAngle=13500_deg100; break;
                case SdrHdlKind::Upper: nHdlAngle=9000_deg100;  break;
                case SdrHdlKind::UpperRight: nHdlAngle=4500_deg100;  break;
                case SdrHdlKind::Right: nHdlAngle=0_deg100;     break;
                default:
                    break;
            }
            // a little bit more (for rounding)
            nHdlAngle = NormAngle36000(nHdlAngle + m_nRotationAngle + 2249_deg100);
            nHdlAngle/=4500_deg100;
            switch (static_cast<sal_uInt8>(nHdlAngle.get())) {
                case 0: ePtr=PointerStyle::ESize;  break;
                case 1: ePtr=PointerStyle::NESize; break;
                case 2: ePtr=PointerStyle::NSize;  break;
                case 3: ePtr=PointerStyle::NWSize; break;
                case 4: ePtr=PointerStyle::WSize;  break;
                case 5: ePtr=PointerStyle::SWSize; break;
                case 6: ePtr=PointerStyle::SSize;  break;
                case 7: ePtr=PointerStyle::SESize; break;
            } // switch
        } else {
            switch (m_eKind) {
                case SdrHdlKind::UpperLeft: ePtr=PointerStyle::NWSize;  break;
                case SdrHdlKind::Upper: ePtr=PointerStyle::NSize;     break;
                case SdrHdlKind::UpperRight: ePtr=PointerStyle::NESize;  break;
                case SdrHdlKind::Left : ePtr=PointerStyle::WSize;     break;
                case SdrHdlKind::Right: ePtr=PointerStyle::ESize;     break;
                case SdrHdlKind::LowerLeft: ePtr=PointerStyle::SWSize;  break;
                case SdrHdlKind::Lower: ePtr=PointerStyle::SSize;     break;
                case SdrHdlKind::LowerRight: ePtr=PointerStyle::SESize;  break;
                case SdrHdlKind::Poly : ePtr=PointerStyle::MovePoint; break;
                case SdrHdlKind::Circle : ePtr=PointerStyle::Hand;      break;
                case SdrHdlKind::Ref1 : ePtr=PointerStyle::RefHand;   break;
                case SdrHdlKind::Ref2 : ePtr=PointerStyle::RefHand;   break;
                case SdrHdlKind::BezierWeight : ePtr=PointerStyle::MoveBezierWeight; break;
                case SdrHdlKind::Glue : ePtr=PointerStyle::MovePoint; break;
                case SdrHdlKind::CustomShape1 : ePtr=PointerStyle::RefHand; break;
                default:
                    break;
            }
        }
    }
    return ePtr;
}

bool SdrHdl::IsFocusHdl() const
{
    switch(m_eKind)
    {
        case SdrHdlKind::UpperLeft:
        case SdrHdlKind::Upper:
        case SdrHdlKind::UpperRight:
        case SdrHdlKind::Left:
        case SdrHdlKind::Right:
        case SdrHdlKind::LowerLeft:
        case SdrHdlKind::Lower:
        case SdrHdlKind::LowerRight:
        {
            // if it's an activated TextEdit, it's moved to extended points
            return !m_pHdlList || !m_pHdlList->IsMoveOutside();
        }

        case SdrHdlKind::Move:      // handle to move object
        case SdrHdlKind::Poly:      // selected point of polygon or curve
        case SdrHdlKind::BezierWeight:      // weight at a curve
        case SdrHdlKind::Circle:      // angle of circle segments, corner radius of rectangles
        case SdrHdlKind::Ref1:      // reference point 1, e. g. center of rotation
        case SdrHdlKind::Ref2:      // reference point 2, e. g. endpoint of reflection axis
        case SdrHdlKind::Glue:      // gluepoint

        // for SJ and the CustomShapeHandles:
        case SdrHdlKind::CustomShape1:

        case SdrHdlKind::User:
        {
            return true;
        }

        default:
        {
            return false;
        }
    }
}

void SdrHdl::onMouseEnter(const MouseEvent& /*rMEvt*/)
{
}

void SdrHdl::onHelpRequest()
{
}

void SdrHdl::onMouseLeave()
{
}

Bitmap SdrHdl::createGluePointBitmap()
{
    return ImpGetBitmap(BitmapMarkerKind::Glue_Deselected, BitmapColorIndex::LightGreen);
}

void SdrHdl::insertNewlyCreatedOverlayObjectForSdrHdl(
    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 GridOffset for non-linear ViewToDevice transformation (calc)
    if(nullptr != GetObj() && rObjectContact.supportsGridOffsets())
    {
        basegfx::B2DVector aOffset(0.0, 0.0);
        const sdr::contact::ViewObjectContact& rVOC(GetObj()->GetViewContact().GetViewObjectContact(
            const_cast<sdr::contact::ObjectContact&>(rObjectContact)));

        rObjectContact.calculateGridOffsetForViewObjectContact(aOffset, rVOC);

        if(!aOffset.equalZero())
        {
            pOverlayObject->setOffset(aOffset);
        }
    }

    // add to OverlayManager
    rOverlayManager.add(*pOverlayObject);

    // add to local OverlayObjectList - ownership change (!)
    maOverlayGroup.append(std::move(pOverlayObject));
}

SdrHdlColor::SdrHdlColor(const Point& rRef, Color aCol, const Size& rSize, bool bLum)
:   SdrHdl(rRef, SdrHdlKind::Color),
    m_aMarkerSize(rSize),
    m_bUseLuminance(bLum)
{
    if(IsUseLuminance())
        aCol = GetLuminance(aCol);

    // remember color
    m_aMarkerColor = aCol;
}

SdrHdlColor::~SdrHdlColor()
{
}

void SdrHdlColor::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();

    if(!m_pHdlList)
        return;

    SdrMarkView* pView = m_pHdlList->GetView();

    if(!pView || pView->areMarkHandlesHidden())
        return;

    SdrPageView* pPageView = pView->GetSdrPageView();

    if(!pPageView)
        return;

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if (xManager.is())
            {
                Bitmap aBmpCol(CreateColorDropper(m_aMarkerColor));
                basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y());
                std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
                    sdr::overlay::OverlayBitmapEx(
                        aPosition,
                        aBmpCol,
                        static_cast<sal_uInt16>(aBmpCol.GetSizePixel().Width() - 1) >> 1,
                        static_cast<sal_uInt16>(aBmpCol.GetSizePixel().Height() - 1) >> 1
                    ));

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pNewOverlayObject),
                    rPageWindow.GetObjectContact(),
                    *xManager);
            }
        }
    }
}

Bitmap SdrHdlColor::CreateColorDropper(Color aCol)
{
    // get the Bitmap
    VclPtr<VirtualDevice> pWrite(VclPtr<VirtualDevice>::Create());
    pWrite->SetOutputSizePixel(m_aMarkerSize);
    pWrite->SetBackground(aCol);
    pWrite->Erase();

    // draw outer border
    sal_Int32 nWidth = m_aMarkerSize.Width();
    sal_Int32 nHeight = m_aMarkerSize.Height();

    pWrite->SetLineColor(COL_LIGHTGRAY);
    pWrite->DrawLine(Point(0, 0), Point(0, nHeight - 1));
    pWrite->DrawLine(Point(1, 0), Point(nWidth - 1, 0));
    pWrite->SetLineColor(COL_GRAY);
    pWrite->DrawLine(Point(1, nHeight - 1), Point(nWidth - 1, nHeight - 1));
    pWrite->DrawLine(Point(nWidth - 1, 1), Point(nWidth - 1, nHeight - 2));

    // draw lighter UpperLeft
    const Color aLightColor(
        static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetRed()) + sal_Int16(0x0040)), sal_Int16(0x00ff))),
        static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetGreen()) + sal_Int16(0x0040)), sal_Int16(0x00ff))),
        static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetBlue()) + sal_Int16(0x0040)), sal_Int16(0x00ff))));
    pWrite->SetLineColor(aLightColor);
    pWrite->DrawLine(Point(1, 1), Point(1, nHeight - 2));
    pWrite->DrawLine(Point(2, 1), Point(nWidth - 2, 1));

    // draw darker LowerRight
    const Color aDarkColor(
        static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetRed()) - sal_Int16(0x0040)), sal_Int16(0x0000))),
        static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetGreen()) - sal_Int16(0x0040)), sal_Int16(0x0000))),
        static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetBlue()) - sal_Int16(0x0040)), sal_Int16(0x0000))));
    pWrite->SetLineColor(aDarkColor);
    pWrite->DrawLine(Point(2, nHeight - 2), Point(nWidth - 2, nHeight - 2));
    pWrite->DrawLine(Point(nWidth - 2, 2), Point(nWidth - 2, nHeight - 3));

    return pWrite->GetBitmap(Point(0,0), m_aMarkerSize);
}

Color SdrHdlColor::GetLuminance(const Color& rCol)
{
    sal_uInt8 aLum = rCol.GetLuminance();
    Color aRetval(aLum, aLum, aLum);
    return aRetval;
}

void SdrHdlColor::SetColor(Color aNew, bool bCallLink)
{
    if(IsUseLuminance())
        aNew = GetLuminance(aNew);

    if(m_aMarkerColor != aNew)
    {
        // remember new color
        m_aMarkerColor = aNew;

        // create new display
        Touch();

        // tell about change
        if(bCallLink)
            m_aColorChangeHdl.Call(this);
    }
}

void SdrHdlColor::SetSize(const Size& rNew)
{
    if(rNew != m_aMarkerSize)
    {
        // remember new size
        m_aMarkerSize = rNew;

        // create new display
        Touch();
    }
}

SdrHdlGradient::SdrHdlGradient(const Point& rRef1, const Point& rRef2, bool bGrad)
    : SdrHdl(rRef1, bGrad ? SdrHdlKind::Gradient : SdrHdlKind::Transparence)
    , m_pColHdl1(nullptr)
    , m_pColHdl2(nullptr)
    , m_a2ndPos(rRef2)
    , m_bGradient(bGrad)
    , m_bMoveSingleHandle(false)
    , m_bMoveFirstHandle(false)
{
}

SdrHdlGradient::~SdrHdlGradient()
{
}

void SdrHdlGradient::Set2ndPos(const Point& rPnt)
{
    if(m_a2ndPos != rPnt)
    {
        // remember new position
        m_a2ndPos = rPnt;

        // create new display
        Touch();
    }
}

void SdrHdlGradient::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();

    if(!m_pHdlList)
        return;

    SdrMarkView* pView = m_pHdlList->GetView();

    if(!pView || pView->areMarkHandlesHidden())
        return;

    SdrPageView* pPageView = pView->GetSdrPageView();

    if(!pPageView)
        return;

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if (xManager.is())
            {
                // striped line in between
                basegfx::B2DVector aVec(m_a2ndPos.X() - m_aPos.X(), m_a2ndPos.Y() - m_aPos.Y());
                double fVecLen = aVec.getLength();
                double fLongPercentArrow = (1.0 - 0.05) * fVecLen;
                double fHalfArrowWidth = (0.05 * 0.5) * fVecLen;
                aVec.normalize();
                basegfx::B2DVector aPerpend(-aVec.getY(), aVec.getX());
                sal_Int32 nMidX = static_cast<sal_Int32>(m_aPos.X() + aVec.getX() * fLongPercentArrow);
                sal_Int32 nMidY = static_cast<sal_Int32>(m_aPos.Y() + aVec.getY() * fLongPercentArrow);
                Point aMidPoint(nMidX, nMidY);

                basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y());
                basegfx::B2DPoint aMidPos(aMidPoint.X(), aMidPoint.Y());

                std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
                    sdr::overlay::OverlayLineStriped(
                        aPosition, aMidPos
                    ));

                pNewOverlayObject->setBaseColor(IsGradient() ? COL_BLACK : COL_BLUE);

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pNewOverlayObject),
                    rPageWindow.GetObjectContact(),
                    *xManager);

                // arrowhead
                Point aLeft(aMidPoint.X() + static_cast<sal_Int32>(aPerpend.getX() * fHalfArrowWidth),
                            aMidPoint.Y() + static_cast<sal_Int32>(aPerpend.getY() * fHalfArrowWidth));
                Point aRight(aMidPoint.X() - static_cast<sal_Int32>(aPerpend.getX() * fHalfArrowWidth),
                            aMidPoint.Y() - static_cast<sal_Int32>(aPerpend.getY() * fHalfArrowWidth));

                basegfx::B2DPoint aPositionLeft(aLeft.X(), aLeft.Y());
                basegfx::B2DPoint aPositionRight(aRight.X(), aRight.Y());
                basegfx::B2DPoint aPosition2(m_a2ndPos.X(), m_a2ndPos.Y());

                pNewOverlayObject.reset(new
                    sdr::overlay::OverlayTriangle(
                        aPositionLeft,
                        aPosition2,
                        aPositionRight,
                        IsGradient() ? COL_BLACK : COL_BLUE
                    ));

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pNewOverlayObject),
                    rPageWindow.GetObjectContact(),
                    *xManager);
            }
        }
    }
}

IMPL_LINK_NOARG(SdrHdlGradient, ColorChangeHdl, SdrHdlColor*, void)
{
    if(GetObj())
        FromIAOToItem(GetObj(), true, true);
}

void SdrHdlGradient::FromIAOToItem(SdrObject* _pObj, bool bSetItemOnObject, bool bUndo)
{
    // from IAO positions and colors to gradient
    const SfxItemSet& rSet = _pObj->GetMergedItemSet();

    GradTransGradient aOldGradTransGradient;
    GradTransGradient aGradTransGradient;
    GradTransVector aGradTransVector;

    aGradTransVector.maPositionA = basegfx::B2DPoint(GetPos().X(), GetPos().Y());
    aGradTransVector.maPositionB = basegfx::B2DPoint(Get2ndPos().X(), Get2ndPos().Y());
    if(m_pColHdl1)
        aGradTransVector.aCol1 = m_pColHdl1->GetColor();
    if(m_pColHdl2)
        aGradTransVector.aCol2 = m_pColHdl2->GetColor();

    if(IsGradient())
        aOldGradTransGradient.aGradient = rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
    else
        aOldGradTransGradient.aGradient = rSet.Get(XATTR_FILLFLOATTRANSPARENCE).GetGradientValue();

    // transform vector data to gradient
    GradTransformer::VecToGrad(aGradTransVector, aGradTransGradient, aOldGradTransGradient, _pObj, m_bMoveSingleHandle, m_bMoveFirstHandle);

    if(bSetItemOnObject)
    {
        SdrModel& rModel(_pObj->getSdrModelFromSdrObject());
        SfxItemSet aNewSet(rModel.GetItemPool());
        const OUString aString;

        if(IsGradient())
        {
            XFillGradientItem aNewGradItem(aString, aGradTransGradient.aGradient);
            aNewSet.Put(aNewGradItem);
        }
        else
        {
            XFillFloatTransparenceItem aNewTransItem(aString, aGradTransGradient.aGradient);
            aNewSet.Put(aNewTransItem);
        }

        if(bUndo && rModel.IsUndoEnabled())
        {
            rModel.BegUndo(SvxResId(IsGradient() ? SIP_XA_FILLGRADIENT : SIP_XA_FILLTRANSPARENCE));
            rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(*_pObj));
            rModel.EndUndo();
        }

        m_pObj->SetMergedItemSetAndBroadcast(aNewSet);
    }

    // back transformation, set values on pIAOHandle
    GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, _pObj);

    SetPos({ basegfx::fround<tools::Long>(aGradTransVector.maPositionA.getX()),
             basegfx::fround<tools::Long>(aGradTransVector.maPositionA.getY()) });
    Set2ndPos({ basegfx::fround<tools::Long>(aGradTransVector.maPositionB.getX()),
                basegfx::fround<tools::Long>(aGradTransVector.maPositionB.getY()) });
    if(m_pColHdl1)
    {
        m_pColHdl1->SetPos({ basegfx::fround<tools::Long>(aGradTransVector.maPositionA.getX()),
                             basegfx::fround<tools::Long>(aGradTransVector.maPositionA.getY()) });
        m_pColHdl1->SetColor(aGradTransVector.aCol1);
    }
    if(m_pColHdl2)
    {
        m_pColHdl2->SetPos({ basegfx::fround<tools::Long>(aGradTransVector.maPositionB.getX()),
                             basegfx::fround<tools::Long>(aGradTransVector.maPositionB.getY()) });
        m_pColHdl2->SetColor(aGradTransVector.aCol2);
    }
}


SdrHdlLine::~SdrHdlLine() {}

void SdrHdlLine::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();

    if(!m_pHdlList)
        return;

    SdrMarkView* pView = m_pHdlList->GetView();

    if(!(pView && !pView->areMarkHandlesHidden() && m_pHdl1 && m_pHdl2))
        return;

    SdrPageView* pPageView = pView->GetSdrPageView();

    if(!pPageView)
        return;

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if (xManager.is())
            {
                basegfx::B2DPoint aPosition1(m_pHdl1->GetPos().X(), m_pHdl1->GetPos().Y());
                basegfx::B2DPoint aPosition2(m_pHdl2->GetPos().X(), m_pHdl2->GetPos().Y());

                std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
                    sdr::overlay::OverlayLineStriped(
                        aPosition1,
                        aPosition2
                    ));

                // color(?)
                pNewOverlayObject->setBaseColor(COL_LIGHTRED);

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pNewOverlayObject),
                    rPageWindow.GetObjectContact(),
                    *xManager);
            }
        }
    }
}

PointerStyle SdrHdlLine::GetPointer() const
{
    return PointerStyle::RefHand;
}


SdrHdlBezWgt::~SdrHdlBezWgt() {}

void SdrHdlBezWgt::CreateB2dIAObject()
{
    // call parent
    SdrHdl::CreateB2dIAObject();

    // create lines
    if(!m_pHdlList)
        return;

    SdrMarkView* pView = m_pHdlList->GetView();

    if(!pView || pView->areMarkHandlesHidden())
        return;

    SdrPageView* pPageView = pView->GetSdrPageView();

    if(!pPageView)
        return;

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if (xManager.is())
            {
                basegfx::B2DPoint aPosition1(m_pHdl1->GetPos().X(), m_pHdl1->GetPos().Y());
                basegfx::B2DPoint aPosition2(m_aPos.X(), m_aPos.Y());

                if(!aPosition1.equal(aPosition2))
                {
                    std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
                        sdr::overlay::OverlayLineStriped(
                            aPosition1,
                            aPosition2
                        ));

                    // line part is not hittable
                    pNewOverlayObject->setHittable(false);

                    // color(?)
                    pNewOverlayObject->setBaseColor(COL_LIGHTBLUE);

                    // OVERLAYMANAGER
                    insertNewlyCreatedOverlayObjectForSdrHdl(
                        std::move(pNewOverlayObject),
                        rPageWindow.GetObjectContact(),
                        *xManager);
                }
            }
        }
    }
}


E3dVolumeMarker::E3dVolumeMarker(const basegfx::B2DPolyPolygon& rWireframePoly)
{
    m_aWireframePoly = rWireframePoly;
}

void E3dVolumeMarker::CreateB2dIAObject()
{
    // create lines
    if(!m_pHdlList)
        return;

    SdrMarkView* pView = m_pHdlList->GetView();

    if(!pView || pView->areMarkHandlesHidden())
        return;

    SdrPageView* pPageView = pView->GetSdrPageView();

    if(!pPageView)
        return;

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if (xManager.is() && m_aWireframePoly.count())
            {
                std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
                    sdr::overlay::OverlayPolyPolygonStripedAndFilled(
                        m_aWireframePoly));

                pNewOverlayObject->setBaseColor(COL_BLACK);

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pNewOverlayObject),
                    rPageWindow.GetObjectContact(),
                    *xManager);
            }
        }
    }
}


ImpEdgeHdl::~ImpEdgeHdl()
{
}

void ImpEdgeHdl::CreateB2dIAObject()
{
    if(m_nObjHdlNum > 1 || !m_pObj)
    {
        // call parent
        SdrHdl::CreateB2dIAObject();
        return;
    }

    // first throw away old one
    GetRidOfIAObject();

    BitmapColorIndex eColIndex = BitmapColorIndex::LightCyan;
    BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_7x7;

    if(!m_pHdlList)
        return;

    SdrMarkView* pView = m_pHdlList->GetView();

    if(!pView || pView->areMarkHandlesHidden())
        return;

    // tdf#159666 Crash when table and line object are selected at the same time
    auto pEdge = dynamic_cast<SdrEdgeObj*>(m_pObj);
    if (!pEdge)
        return;

    if(pEdge->GetConnectedNode(m_nObjHdlNum == 0) != nullptr)
        eColIndex = BitmapColorIndex::LightRed;

    if(m_nPPntNum < 2)
    {
        // Handle with plus sign inside
        eKindOfMarker = BitmapMarkerKind::Circ_7x7;
    }

    SdrPageView* pPageView = pView->GetSdrPageView();
    if(!pPageView)
        return;

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if (xManager.is())
            {
                basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y());
                std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(CreateOverlayObject(
                    aPosition,
                    eColIndex,
                    eKindOfMarker));

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pNewOverlayObject),
                    rPageWindow.GetObjectContact(),
                    *xManager);
            }
        }
    }
}

void ImpEdgeHdl::SetLineCode(SdrEdgeLineCode eCode)
{
    if(m_eLineCode != eCode)
    {
        // remember new value
        m_eLineCode = eCode;

        // create new display
        Touch();
    }
}

PointerStyle ImpEdgeHdl::GetPointer() const
{
    SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( m_pObj );
    if (pEdge==nullptr)
        return SdrHdl::GetPointer();
    if (m_nObjHdlNum<=1)
        return PointerStyle::MovePoint;
    if (IsHorzDrag())
        return PointerStyle::ESize;
    else
        return PointerStyle::SSize;
}

bool ImpEdgeHdl::IsHorzDrag() const
{
    SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( m_pObj );
    if (pEdge==nullptr)
        return false;
    if (m_nObjHdlNum<=1)
        return false;

    SdrEdgeKind eEdgeKind = pEdge->GetObjectItem(SDRATTR_EDGEKIND).GetValue();

    const SdrEdgeInfoRec& rInfo=pEdge->m_aEdgeInfo;
    if (eEdgeKind==SdrEdgeKind::OrthoLines || eEdgeKind==SdrEdgeKind::Bezier)
    {
        return !rInfo.ImpIsHorzLine(m_eLineCode,*pEdge->m_pEdgeTrack);
    }
    else if (eEdgeKind==SdrEdgeKind::ThreeLines)
    {
        tools::Long nAngle=m_nObjHdlNum==2 ? rInfo.m_nAngle1 : rInfo.m_nAngle2;
        return nAngle==0 || nAngle==18000;
    }
    return false;
}


ImpMeasureHdl::~ImpMeasureHdl()
{
}

void ImpMeasureHdl::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();

    if(!m_pHdlList)
        return;

    SdrMarkView* pView = m_pHdlList->GetView();

    if(!pView || pView->areMarkHandlesHidden())
        return;

    BitmapColorIndex eColIndex = BitmapColorIndex::LightCyan;
    BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_9x9;

    if(m_nObjHdlNum > 1)
    {
        eKindOfMarker = BitmapMarkerKind::Rect_7x7;
    }

    if(m_bSelect)
    {
        eColIndex = BitmapColorIndex::Cyan;
    }

    SdrPageView* pPageView = pView->GetSdrPageView();

    if(!pPageView)
        return;

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if (xManager.is())
            {
                basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y());
                std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(CreateOverlayObject(
                    aPosition,
                    eColIndex,
                    eKindOfMarker));

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pNewOverlayObject),
                    rPageWindow.GetObjectContact(),
                    *xManager);
            }
        }
    }
}

PointerStyle ImpMeasureHdl::GetPointer() const
{
    switch (m_nObjHdlNum)
    {
        case 0: case 1: return PointerStyle::Hand;
        case 2: case 3: return PointerStyle::MovePoint;
        case 4: case 5: return SdrHdl::GetPointer(); // will then be rotated appropriately
    } // switch
    return PointerStyle::NotAllowed;
}


ImpTextframeHdl::ImpTextframeHdl(const tools::Rectangle& rRect) :
    SdrHdl(rRect.TopLeft(),SdrHdlKind::Move),
    maRect(rRect)
{
}

void ImpTextframeHdl::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();

    if(!m_pHdlList)
        return;

    SdrMarkView* pView = m_pHdlList->GetView();

    if(!pView || pView->areMarkHandlesHidden())
        return;

    SdrPageView* pPageView = pView->GetSdrPageView();

    if(!pPageView)
        return;

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if (xManager.is())
            {
                const basegfx::B2DPoint aTopLeft(maRect.Left(), maRect.Top());
                const basegfx::B2DPoint aBottomRight(maRect.Right(), maRect.Bottom());
                const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
                const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);

                std::unique_ptr<sdr::overlay::OverlayRectangle> pNewOverlayObject(new sdr::overlay::OverlayRectangle(
                    aTopLeft,
                    aBottomRight,
                    aHilightColor,
                    fTransparence,
                    3.0,
                    3.0,
                    -toRadians(m_nRotationAngle),
                    true)); // allow animation; the Handle is not shown at text edit time

                pNewOverlayObject->setHittable(false);

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pNewOverlayObject),
                    rPageWindow.GetObjectContact(),
                    *xManager);
            }
        }
    }
}


static bool ImpSdrHdlListSorter(std::unique_ptr<SdrHdl> const& lhs, std::unique_ptr<SdrHdl> const& rhs)
{
    SdrHdlKind eKind1=lhs->GetKind();
    SdrHdlKind eKind2=rhs->GetKind();
    // Level 1: first normal handles, then Glue, then User, then Plus handles, then reference point handles
    unsigned n1=1;
    unsigned n2=1;
    if (eKind1!=eKind2)
    {
        if (eKind1==SdrHdlKind::Ref1 || eKind1==SdrHdlKind::Ref2 || eKind1==SdrHdlKind::MirrorAxis) n1=5;
        else if (eKind1==SdrHdlKind::Glue) n1=2;
        else if (eKind1==SdrHdlKind::User) n1=3;
        else if (eKind1==SdrHdlKind::SmartTag) n1=0;
        if (eKind2==SdrHdlKind::Ref1 || eKind2==SdrHdlKind::Ref2 || eKind2==SdrHdlKind::MirrorAxis) n2=5;
        else if (eKind2==SdrHdlKind::Glue) n2=2;
        else if (eKind2==SdrHdlKind::User) n2=3;
        else if (eKind2==SdrHdlKind::SmartTag) n2=0;
    }
    if (lhs->IsPlusHdl()) n1=4;
    if (rhs->IsPlusHdl()) n2=4;
    if (n1==n2)
    {
        // Level 2: PageView (Pointer)
        SdrPageView* pPV1=lhs->GetPageView();
        SdrPageView* pPV2=rhs->GetPageView();
        if (pPV1==pPV2)
        {
            // Level 3: Position (x+y)
            SdrObject* pObj1=lhs->GetObj();
            SdrObject* pObj2=rhs->GetObj();
            if (pObj1==pObj2)
            {
                sal_uInt32 nNum1=lhs->GetObjHdlNum();
                sal_uInt32 nNum2=rhs->GetObjHdlNum();
                if (nNum1==nNum2)
                {
                    if (eKind1==eKind2)
                        return lhs<rhs; // Hack, to always get to the same sorting
                    return static_cast<sal_uInt16>(eKind1)<static_cast<sal_uInt16>(eKind2);
                }
                else
                    return nNum1<nNum2;
            }
            else
            {
                return pObj1<pObj2;
            }
        }
        else
        {
            return pPV1<pPV2;
        }
    }
    else
    {
        return n1<n2;
    }
}

namespace {

// Helper struct for re-sorting handles
struct ImplHdlAndIndex
{
    SdrHdl*                     mpHdl;
    sal_uInt32                  mnIndex;
};

}

extern "C" {

// Helper method for sorting handles taking care of OrdNums, keeping order in
// single objects and re-sorting polygon handles intuitively
static int ImplSortHdlFunc( const void* pVoid1, const void* pVoid2 )
{
    const ImplHdlAndIndex* p1 = static_cast<ImplHdlAndIndex const *>(pVoid1);
    const ImplHdlAndIndex* p2 = static_cast<ImplHdlAndIndex const *>(pVoid2);

    if(p1->mpHdl->GetObj() == p2->mpHdl->GetObj())
    {
        if(p1->mpHdl->GetObj() && dynamic_cast<const SdrPathObj*>(p1->mpHdl->GetObj()) != nullptr)
        {
            // same object and a path object
            if((p1->mpHdl->GetKind() == SdrHdlKind::Poly || p1->mpHdl->GetKind() == SdrHdlKind::BezierWeight)
                && (p2->mpHdl->GetKind() == SdrHdlKind::Poly || p2->mpHdl->GetKind() == SdrHdlKind::BezierWeight))
            {
                // both handles are point or control handles
                if(p1->mpHdl->GetPolyNum() == p2->mpHdl->GetPolyNum())
                {
                    if(p1->mpHdl->GetPointNum() < p2->mpHdl->GetPointNum())
                    {
                        return -1;
                    }
                    else
                    {
                        return 1;
                    }
                }
                else if(p1->mpHdl->GetPolyNum() < p2->mpHdl->GetPolyNum())
                {
                    return -1;
                }
                else
                {
                    return 1;
                }
            }
        }
    }
    else
    {
        if(!p1->mpHdl->GetObj())
        {
            return -1;
        }
        else if(!p2->mpHdl->GetObj())
        {
            return 1;
        }
        else
        {
            // different objects, use OrdNum for sort
            const sal_uInt32 nOrdNum1 = p1->mpHdl->GetObj()->GetOrdNum();
            const sal_uInt32 nOrdNum2 = p2->mpHdl->GetObj()->GetOrdNum();

            if(nOrdNum1 < nOrdNum2)
            {
                return -1;
            }
            else
            {
                return 1;
            }
        }
    }

    // fallback to indices
    if(p1->mnIndex < p2->mnIndex)
    {
        return -1;
    }
    else
    {
        return 1;
    }
}

}

void SdrHdlList::TravelFocusHdl(bool bForward)
{
    // security correction
    if (mnFocusIndex >= GetHdlCount())
        mnFocusIndex = SAL_MAX_SIZE;

    if(maList.empty())
        return;

    // take care of old handle
    const size_t nOldHdlNum(mnFocusIndex);
    SdrHdl* pOld = nullptr;
    if (nOldHdlNum < GetHdlCount())
        pOld = GetHdl(nOldHdlNum);

    if(pOld)
    {
        // switch off old handle
        mnFocusIndex = SAL_MAX_SIZE;
        pOld->Touch();
    }

    // allocate pointer array for sorted handle list
    std::unique_ptr<ImplHdlAndIndex[]> pHdlAndIndex(new ImplHdlAndIndex[maList.size()]);

    // build sorted handle list
    for( size_t a = 0; a < maList.size(); ++a)
    {
        pHdlAndIndex[a].mpHdl = maList[a].get();
        pHdlAndIndex[a].mnIndex = a;
    }

    qsort(pHdlAndIndex.get(), maList.size(), sizeof(ImplHdlAndIndex), ImplSortHdlFunc);

    // look for old num in sorted array
    size_t nOldHdl(nOldHdlNum);

    if(nOldHdlNum != SAL_MAX_SIZE)
    {
        for(size_t a = 0; a < maList.size(); ++a)
        {
            if(pHdlAndIndex[a].mpHdl == pOld)
            {
                nOldHdl = a;
                break;
            }
        }
    }

    // build new HdlNum
    size_t nNewHdl(nOldHdl);

    // do the focus travel
    if(bForward)
    {
        if(nOldHdl != SAL_MAX_SIZE)
        {
            if(nOldHdl == maList.size() - 1)
            {
                // end forward run
                nNewHdl = SAL_MAX_SIZE;
            }
            else
            {
                // simply the next handle
                nNewHdl++;
            }
        }
        else
        {
            // start forward run at first entry
            nNewHdl = 0;
        }
    }
    else
    {
        if(nOldHdl == SAL_MAX_SIZE)
        {
            // start backward run at last entry
            nNewHdl = maList.size() - 1;

        }
        else
        {
            if(nOldHdl == 0)
            {
                // end backward run
                nNewHdl = SAL_MAX_SIZE;
            }
            else
            {
                // simply the previous handle
                nNewHdl--;
            }
        }
    }

    // build new HdlNum
    sal_uIntPtr nNewHdlNum(nNewHdl);

    // look for old num in sorted array
    if(nNewHdl != SAL_MAX_SIZE)
    {
        SdrHdl* pNew = pHdlAndIndex[nNewHdl].mpHdl;

        for(size_t a = 0; a < maList.size(); ++a)
        {
            if(maList[a].get() == pNew)
            {
                nNewHdlNum = a;
                break;
            }
        }
    }

    // take care of next handle
    if(nOldHdlNum != nNewHdlNum)
    {
        mnFocusIndex = nNewHdlNum;
        if (mnFocusIndex < GetHdlCount())
        {
            SdrHdl* pNew = GetHdl(mnFocusIndex);
            pNew->Touch();
        }
    }
}

SdrHdl* SdrHdlList::GetFocusHdl() const
{
    if(mnFocusIndex < GetHdlCount())
        return GetHdl(mnFocusIndex);
    else
        return nullptr;
}

void SdrHdlList::SetFocusHdl(SdrHdl* pNew)
{
    if(!pNew)
        return;

    SdrHdl* pActual = GetFocusHdl();

    if(pActual && pActual == pNew)
        return;

    const size_t nNewHdlNum = GetHdlNum(pNew);

    if(nNewHdlNum != SAL_MAX_SIZE)
    {
        mnFocusIndex = nNewHdlNum;

        if(pActual)
        {
            pActual->Touch();
        }

        pNew->Touch();
    }
}

void SdrHdlList::ResetFocusHdl()
{
    SdrHdl* pHdl = GetFocusHdl();

    mnFocusIndex = SAL_MAX_SIZE;

    if(pHdl)
    {
        pHdl->Touch();
    }
}


SdrHdlList::SdrHdlList(SdrMarkView* pV)
:   mnFocusIndex(SAL_MAX_SIZE),
    m_pView(pV)
{
    m_nHdlSize = 3;
    m_bRotateShear = false;
    m_bMoveOutside = false;
    m_bDistortShear = false;
}

SdrHdlList::~SdrHdlList()
{
    Clear();
}

void SdrHdlList::SetHdlSize(sal_uInt16 nSiz)
{
    if(m_nHdlSize != nSiz)
    {
        // remember new value
        m_nHdlSize = nSiz;

        // propagate change to IAOs
        for(size_t i=0; i<GetHdlCount(); ++i)
        {
            SdrHdl* pHdl = GetHdl(i);
            pHdl->Touch();
        }
    }
}

void SdrHdlList::SetMoveOutside(bool bOn)
{
    if(m_bMoveOutside != bOn)
    {
        // remember new value
        m_bMoveOutside = bOn;

        // propagate change to IAOs
        for(size_t i=0; i<GetHdlCount(); ++i)
        {
            SdrHdl* pHdl = GetHdl(i);
            pHdl->Touch();
        }
    }
}

void SdrHdlList::SetRotateShear(bool bOn)
{
    m_bRotateShear = bOn;
}

void SdrHdlList::SetDistortShear(bool bOn)
{
    m_bDistortShear = bOn;
}

std::unique_ptr<SdrHdl> SdrHdlList::RemoveHdl(size_t nNum)
{
    std::unique_ptr<SdrHdl> pRetval = std::move(maList[nNum]);
    maList.erase(maList.begin() + nNum);

    return pRetval;
}

void SdrHdlList::RemoveAllByKind(SdrHdlKind eKind)
{
    std::erase_if(maList, [&eKind](std::unique_ptr<SdrHdl>& rItem) { return rItem->GetKind() == eKind; });
}

void SdrHdlList::Clear()
{
    maList.clear();

    m_bRotateShear=false;
    m_bDistortShear=false;
}

void SdrHdlList::Sort()
{
    // remember currently focused handle
    SdrHdl* pPrev = GetFocusHdl();

    std::sort( maList.begin(), maList.end(), ImpSdrHdlListSorter );

    // get now and compare
    SdrHdl* pNow = GetFocusHdl();

    if(pPrev == pNow)
        return;

    if(pPrev)
    {
        pPrev->Touch();
    }

    if(pNow)
    {
        pNow->Touch();
    }
}

size_t SdrHdlList::GetHdlNum(const SdrHdl* pHdl) const
{
    if (pHdl==nullptr)
        return SAL_MAX_SIZE;
    auto it = std::find_if( maList.begin(), maList.end(),
        [&](const std::unique_ptr<SdrHdl> & p) { return p.get() == pHdl; });
    assert(it != maList.end());
    if( it == maList.end() )
        return SAL_MAX_SIZE;
    return it - maList.begin();
}

void SdrHdlList::AddHdl(std::unique_ptr<SdrHdl> pHdl)
{
    assert(pHdl);
    pHdl->SetHdlList(this);
    maList.push_back(std::move(pHdl));
}

SdrHdl* SdrHdlList::IsHdlListHit(const Point& rPnt) const
{
    SdrHdl* pRet=nullptr;
    const size_t nCount=GetHdlCount();
    size_t nNum=nCount;
    while (nNum>0 && pRet==nullptr)
    {
        nNum--;
        SdrHdl* pHdl=GetHdl(nNum);
        if (pHdl->IsHdlHit(rPnt))
            pRet=pHdl;
    }
    return pRet;
}

SdrHdl* SdrHdlList::GetHdl(SdrHdlKind eKind1) const
{
    SdrHdl* pRet=nullptr;
    for (size_t i=0; i<GetHdlCount() && pRet==nullptr; ++i)
    {
        SdrHdl* pHdl=GetHdl(i);
        if (pHdl->GetKind()==eKind1)
            pRet=pHdl;
    }
    return pRet;
}

void SdrHdlList::MoveTo(SdrHdlList& rOther)
{
    for (auto & pHdl : maList)
        pHdl->SetHdlList(&rOther);
    rOther.maList.insert(rOther.maList.end(),
        std::make_move_iterator(maList.begin()), std::make_move_iterator(maList.end()));
    maList.clear();
}

SdrCropHdl::SdrCropHdl(
    const Point& rPnt,
    SdrHdlKind eNewKind,
    double fShearX,
    double fRotation)
:   SdrHdl(rPnt, eNewKind),
    mfShearX(fShearX),
    mfRotation(fRotation)
{
}


Bitmap SdrCropHdl::GetBitmapForHandle( const Bitmap& rBitmap, int nSize )
{
    int nPixelSize = 0, nX = 0, nY = 0, nOffset = 0;

    if( nSize <= 3 )
    {
        nPixelSize = 13;
        nOffset = 0;
    }
    else if( nSize <=4 )
    {
        nPixelSize = 17;
        nOffset = 39;
    }
    else
    {
        nPixelSize = 21;
        nOffset = 90;
    }

    switch( m_eKind )
    {
        case SdrHdlKind::UpperLeft: nX = 0; nY = 0; break;
        case SdrHdlKind::Upper: nX = 1; nY = 0; break;
        case SdrHdlKind::UpperRight: nX = 2; nY = 0; break;
        case SdrHdlKind::Left:  nX = 0; nY = 1; break;
        case SdrHdlKind::Right: nX = 2; nY = 1; break;
        case SdrHdlKind::LowerLeft: nX = 0; nY = 2; break;
        case SdrHdlKind::Lower: nX = 1; nY = 2; break;
        case SdrHdlKind::LowerRight: nX = 2; nY = 2; break;
        default: break;
    }

    tools::Rectangle aSourceRect( Point( nX * nPixelSize + nOffset,  nY * nPixelSize), Size(nPixelSize, nPixelSize) );

    Bitmap aRetval(rBitmap);
    aRetval.Crop(aSourceRect);
    return aRetval;
}


void SdrCropHdl::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();

    SdrMarkView* pView = m_pHdlList ? m_pHdlList->GetView() : nullptr;
    SdrPageView* pPageView = pView ? pView->GetSdrPageView() : nullptr;

    if( !pPageView || pView->areMarkHandlesHidden() )
        return;

    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
    int nHdlSize = m_pHdlList->GetHdlSize();

    const Bitmap aHandlesBitmap(SIP_SA_CROP_MARKERS);
    Bitmap aBmp1( GetBitmapForHandle( aHandlesBitmap, nHdlSize ) );

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if (xManager.is())
            {
                basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y());

                std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject;

                // animate focused handles
                if(IsFocusHdl() && (m_pHdlList->GetFocusHdl() == this))
                {
                    if( nHdlSize >= 2 )
                        nHdlSize = 1;

                    Bitmap aBmp2( GetBitmapForHandle( aHandlesBitmap, nHdlSize + 1 ) );

                    const sal_uInt64 nBlinkTime = rStyleSettings.GetCursorBlinkTime();

                    pOverlayObject.reset(new sdr::overlay::OverlayAnimatedBitmapEx(
                        aPosition,
                        aBmp1,
                        aBmp2,
                        nBlinkTime,
                        static_cast<sal_uInt16>(aBmp1.GetSizePixel().Width() - 1) >> 1,
                        static_cast<sal_uInt16>(aBmp1.GetSizePixel().Height() - 1) >> 1,
                        static_cast<sal_uInt16>(aBmp2.GetSizePixel().Width() - 1) >> 1,
                        static_cast<sal_uInt16>(aBmp2.GetSizePixel().Height() - 1) >> 1,
                        mfShearX,
                        mfRotation));
                }
                else
                {
                    // create centered handle as default
                    pOverlayObject.reset(new sdr::overlay::OverlayBitmapEx(
                        aPosition,
                        aBmp1,
                        static_cast<sal_uInt16>(aBmp1.GetSizePixel().Width() - 1) >> 1,
                        static_cast<sal_uInt16>(aBmp1.GetSizePixel().Height() - 1) >> 1,
                        0.0,
                        mfShearX,
                        mfRotation));
                }

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pOverlayObject),
                    rPageWindow.GetObjectContact(),
                    *xManager);
            }
        }
    }
}


// with the correction of crop handling I could get rid of the extra mirroring flag, adapted stuff
// accordingly

SdrCropViewHdl::SdrCropViewHdl(
    basegfx::B2DHomMatrix aObjectTransform,
    Graphic aGraphic,
    double fCropLeft,
    double fCropTop,
    double fCropRight,
    double fCropBottom)
:   SdrHdl(Point(), SdrHdlKind::User),
    maObjectTransform(std::move(aObjectTransform)),
    maGraphic(std::move(aGraphic)),
    mfCropLeft(fCropLeft),
    mfCropTop(fCropTop),
    mfCropRight(fCropRight),
    mfCropBottom(fCropBottom)
{
}

namespace {

void translateRotationToMirroring(basegfx::B2DVector & scale, double * rotate) {
    assert(rotate != nullptr);

    // detect 180 degree rotation, this is the same as mirrored in X and Y,
    // thus change to mirroring. Prefer mirroring here. Use the equal call
    // with getSmallValue here, the original which uses rtl::math::approxEqual
    // is too correct here. Maybe this changes with enhanced precision in aw080
    // to the better so that this can be reduced to the more precise call again
    if(basegfx::fTools::equal(fabs(*rotate), M_PI, 0.000000001))
    {
        scale.setX(scale.getX() * -1.0);
        scale.setY(scale.getY() * -1.0);
        *rotate = 0.0;
    }
}

}

void SdrCropViewHdl::CreateB2dIAObject()
{
    GetRidOfIAObject();
    SdrMarkView* pView = m_pHdlList ? m_pHdlList->GetView() : nullptr;
    SdrPageView* pPageView = pView ? pView->GetSdrPageView() : nullptr;

    if(!pPageView || pView->areMarkHandlesHidden())
    {
        return;
    }

    // decompose to have current translate and scale
    basegfx::B2DVector aScale, aTranslate;
    double fRotate, fShearX;

    maObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);

    if(aScale.equalZero())
    {
        return;
    }

    translateRotationToMirroring(aScale, &fRotate);

    // remember mirroring, reset at Scale and adapt crop values for usage;
    // mirroring can stay in the object transformation, so do not have to
    // cope with it here (except later for the CroppedImage transformation,
    // see below)
    const bool bMirroredX(aScale.getX() < 0.0);
    const bool bMirroredY(aScale.getY() < 0.0);
    double fCropLeft(mfCropLeft);
    double fCropTop(mfCropTop);
    double fCropRight(mfCropRight);
    double fCropBottom(mfCropBottom);

    if(bMirroredX)
    {
        aScale.setX(-aScale.getX());
    }

    if(bMirroredY)
    {
        aScale.setY(-aScale.getY());
    }

    // create target translate and scale
    const basegfx::B2DVector aTargetScale(
        aScale.getX() + fCropRight + fCropLeft,
        aScale.getY() + fCropBottom + fCropTop);
    const basegfx::B2DVector aTargetTranslate(
        aTranslate.getX() - fCropLeft,
        aTranslate.getY() - fCropTop);

    // create ranges to make comparisons
    const basegfx::B2DRange aCurrentForCompare(
        aTranslate.getX(), aTranslate.getY(),
        aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY());
    basegfx::B2DRange aCropped(
        aTargetTranslate.getX(), aTargetTranslate.getY(),
        aTargetTranslate.getX() + aTargetScale.getX(), aTargetTranslate.getY() + aTargetScale.getY());

    if(aCropped.isEmpty())
    {
        // nothing to return since cropped content is completely empty
        return;
    }

    if(aCurrentForCompare.equal(aCropped))
    {
        // no crop at all
        return;
    }

    // back-transform to have values in unit coordinates
    basegfx::B2DHomMatrix aBackToUnit;
    aBackToUnit.translate(-aTranslate.getX(), -aTranslate.getY());
    aBackToUnit.scale(
        basegfx::fTools::equalZero(aScale.getX()) ? 1.0 : 1.0 / aScale.getX(),
        basegfx::fTools::equalZero(aScale.getY()) ? 1.0 : 1.0 / aScale.getY());

    // transform cropped back to unit coordinates
    aCropped.transform(aBackToUnit);

    // prepare crop PolyPolygon
    basegfx::B2DPolygon aGraphicOutlinePolygon(
        basegfx::utils::createPolygonFromRect(
            aCropped));
    basegfx::B2DPolyPolygon aCropPolyPolygon(aGraphicOutlinePolygon);

    // current range is unit range
    basegfx::B2DRange aOverlap(0.0, 0.0, 1.0, 1.0);

    aOverlap.intersect(aCropped);

    if(!aOverlap.isEmpty())
    {
        aCropPolyPolygon.append(
            basegfx::utils::createPolygonFromRect(
                aOverlap));
    }

    // transform to object coordinates to prepare for clip
    aCropPolyPolygon.transform(maObjectTransform);
    aGraphicOutlinePolygon.transform(maObjectTransform);

    // create cropped transformation
    basegfx::B2DHomMatrix aCroppedTransform;

    aCroppedTransform.scale(
        aCropped.getWidth(),
        aCropped.getHeight());
    aCroppedTransform.translate(
        aCropped.getMinX(),
        aCropped.getMinY());
    aCroppedTransform = maObjectTransform * aCroppedTransform;

    // prepare graphic primitive (transformed)
    const drawinglayer::primitive2d::Primitive2DReference aGraphic(
        new drawinglayer::primitive2d::GraphicPrimitive2D(
            aCroppedTransform,
            maGraphic));

    // prepare outline polygon for whole graphic
    const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor());
    const drawinglayer::primitive2d::Primitive2DReference aGraphicOutline(
        new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
        std::move(aGraphicOutlinePolygon),
        aHilightColor));

    // combine these
    drawinglayer::primitive2d::Primitive2DContainer aCombination { aGraphic, aGraphicOutline };

    // embed to MaskPrimitive2D
    const drawinglayer::primitive2d::Primitive2DReference aMaskedGraphic(
        new drawinglayer::primitive2d::MaskPrimitive2D(
            std::move(aCropPolyPolygon),
            std::move(aCombination)));

    // embed to UnifiedTransparencePrimitive2D
    const drawinglayer::primitive2d::Primitive2DReference aTransparenceMaskedGraphic(
        new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
            drawinglayer::primitive2d::Primitive2DContainer { aMaskedGraphic },
            0.8));

    const drawinglayer::primitive2d::Primitive2DContainer aSequence { aTransparenceMaskedGraphic };

    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
    {
        // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b];
        const SdrPageWindow& rPageWindow = *(pPageView->GetPageWindow(b));

        if(rPageWindow.GetPaintWindow().OutputToWindow())
        {
            const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
            if(xManager.is())
            {
                std::unique_ptr<sdr::overlay::OverlayObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(drawinglayer::primitive2d::Primitive2DContainer(aSequence)));

                // only informative object, no hit
                pNew->setHittable(false);

                // OVERLAYMANAGER
                insertNewlyCreatedOverlayObjectForSdrHdl(
                    std::move(pNew),
                    rPageWindow.GetObjectContact(),
                    *xManager);
            }
        }
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
