/* -*- 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/svdetc.hxx>
#include <svx/svdmrkv.hxx>
#include <vcl/window.hxx>
#include <vcl/settings.hxx>
#include <vcl/virdev.hxx>
#include <tools/poly.hxx>
#include <vcl/bitmapaccess.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 <svx/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 <vcl/lazydelete.hxx>
#include <vcl/BitmapTools.hxx>
 
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <memory>
#include <bitmaps.hlst>
 
// #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
    BitmapEx                    maMarkersBitmap;
 
    // the cropped Bitmaps for reusage
    ::std::vector< BitmapEx >   maRealMarkers;
 
    // helpers
    BitmapEx& impGetOrCreateTargetBitmap(sal_uInt16 nIndex, const tools::Rectangle& rRectangle);
 
public:
    explicit SdrHdlBitmapSet();
 
    const BitmapEx& GetBitmapEx(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)
{
}
 
BitmapEx& SdrHdlBitmapSet::impGetOrCreateTargetBitmap(sal_uInt16 nIndex, const tools::Rectangle& rRectangle)
{
    BitmapEx& rTargetBitmap = maRealMarkers[nIndex];
 
    if(rTargetBitmap.IsEmpty())
    {
        rTargetBitmap = maMarkersBitmap;
        rTargetBitmap.Crop(rRectangle);
    }
 
    return rTargetBitmap;
}
 
// change getting of bitmap to use the big resource bitmap
const BitmapEx& SdrHdlBitmapSet::GetBitmapEx(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." );
            SAL_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():
    pObj(nullptr),
    pPV(nullptr),
    pHdlList(nullptr),
    eKind(SdrHdlKind::Move),
    nRotationAngle(0),
    nObjHdlNum(0),
    nPolyNum(0),
    nPPntNum(0),
    nSourceHdlNum(0),
    bSelect(false),
    b1PixMore(false),
    bPlusHdl(false),
    mbMoveOutside(false),
    mbMouseOver(false)
{
}
 
SdrHdl::SdrHdl(const Point& rPnt, SdrHdlKind eNewKind):
    pObj(nullptr),
    pPV(nullptr),
    pHdlList(nullptr),
    aPos(rPnt),
    eKind(eNewKind),
    nRotationAngle(0),
    nObjHdlNum(0),
    nPolyNum(0),
    nPPntNum(0),
    nSourceHdlNum(0),
    bSelect(false),
    b1PixMore(false),
    bPlusHdl(false),
    mbMoveOutside(false),
    mbMouseOver(false)
{
}
 
SdrHdl::~SdrHdl()
{
    GetRidOfIAObject();
}
 
void SdrHdl::Set1PixMore(bool bJa)
{
    if(b1PixMore != bJa)
    {
        b1PixMore = bJa;
 
        // create new display
        Touch();
    }
}
 
void SdrHdl::SetMoveOutside( bool bMoveOutside )
{
    if(mbMoveOutside != bMoveOutside)
    {
        mbMoveOutside = bMoveOutside;
 
        // create new display
        Touch();
    }
}
 
void SdrHdl::SetRotationAngle(long n)
{
    if(nRotationAngle != n)
    {
        nRotationAngle = n;
 
        // create new display
        Touch();
    }
}
 
void SdrHdl::SetPos(const Point& rPnt)
{
    if(aPos != rPnt)
    {
        // remember new position
        aPos = rPnt;
 
        // create new display
        Touch();
    }
}
 
void SdrHdl::SetSelected(bool bJa)
{
    if(bSelect != bJa)
    {
        // remember new value
        bSelect = bJa;
 
        // create new display
        Touch();
    }
}
 
void SdrHdl::SetHdlList(SdrHdlList* pList)
{
    if(pHdlList != pList)
    {
        // remember list
        pHdlList = pList;
 
        // now its possible to create graphic representation
        Touch();
    }
}
 
void SdrHdl::SetObj(SdrObject* pNewObj)
{
    if(pObj != pNewObj)
    {
        // remember new object
        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(pHdlList && pHdlList->GetView() && !pHdlList->GetView()->areMarkHandlesHidden())
    {
        BitmapColorIndex eColIndex = BitmapColorIndex::LightGreen;
        BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_7x7;
 
        bool bRot = pHdlList->IsRotateShear();
        if(pObj)
            eColIndex = bSelect ? BitmapColorIndex::Cyan : BitmapColorIndex::LightCyan;
        if(bRot)
        {
            // red rotation handles
            if(pObj && bSelect)
                eColIndex = BitmapColorIndex::Red;
            else
                eColIndex = BitmapColorIndex::LightRed;
        }
 
        switch(eKind)
        {
            case SdrHdlKind::Move:
            {
                eKindOfMarker = (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 = b1PixMore ? BitmapMarkerKind::Circ_9x9 : BitmapMarkerKind::Circ_7x7;
                }
                else
                {
                    eKindOfMarker = 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 = b1PixMore ? BitmapMarkerKind::Customshape_9x9 : BitmapMarkerKind::Customshape_7x7;
                eColIndex = BitmapColorIndex::Yellow;
                break;
            }
            default:
                break;
        }
 
        SdrMarkView* pView = pHdlList->GetView();
        SdrPageView* pPageView = pView->GetSdrPageView();
 
        if(pPageView)
        {
            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(pHdlList->IsMoveOutside() || mbMoveOutside)
                    {
                        Size aOffset = rOutDev.PixelToLogic(Size(4, 4));
 
                        if(eKind == SdrHdlKind::UpperLeft || eKind == SdrHdlKind::Upper || eKind == SdrHdlKind::UpperRight)
                            aMoveOutsideOffset.AdjustY( -(aOffset.Width()) );
                        if(eKind == SdrHdlKind::LowerLeft || eKind == SdrHdlKind::Lower || eKind == SdrHdlKind::LowerRight)
                            aMoveOutsideOffset.AdjustY(aOffset.Height() );
                        if(eKind == SdrHdlKind::UpperLeft || eKind == SdrHdlKind::Left  || eKind == SdrHdlKind::LowerLeft)
                            aMoveOutsideOffset.AdjustX( -(aOffset.Width()) );
                        if(eKind == SdrHdlKind::UpperRight || eKind == SdrHdlKind::Right || eKind == SdrHdlKind::LowerRight)
                            aMoveOutsideOffset.AdjustX(aOffset.Height() );
                    }
 
                    rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
                    if (xManager.is())
                    {
                        basegfx::B2DPoint aPosition(aPos.X(), 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
                        if (pNewOverlayObject)
                        {
                            xManager->add(*pNewOverlayObject);
                            maOverlayGroup.append(std::move(pNewOverlayObject));
                        }
                    }
                }
            }
        }
    }
}
 
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 OUString("rect7");
        case BitmapMarkerKind::Rect_9x9:
            return OUString("rect9");
        case BitmapMarkerKind::Rect_11x11:
            return OUString("rect11");
        case BitmapMarkerKind::Rect_13x13:
            return OUString("rect13");
        case BitmapMarkerKind::Circ_7x7:
        case BitmapMarkerKind::Customshape_7x7:
            return OUString("circ7");
        case BitmapMarkerKind::Circ_9x9:
        case BitmapMarkerKind::Customshape_9x9:
            return OUString("circ9");
        case BitmapMarkerKind::Circ_11x11:
        case BitmapMarkerKind::Customshape_11x11:
            return OUString("circ11");
        case BitmapMarkerKind::Elli_7x9:
            return OUString("elli7x9");
        case BitmapMarkerKind::Elli_9x11:
            return OUString("elli9x11");
        case BitmapMarkerKind::Elli_9x7:
            return OUString("elli9x7");
        case BitmapMarkerKind::Elli_11x9:
            return OUString("elli11x9");
        case BitmapMarkerKind::RectPlus_7x7:
            return OUString("rectplus7");
        case BitmapMarkerKind::RectPlus_9x9:
            return OUString("rectplus9");
        case BitmapMarkerKind::RectPlus_11x11:
            return OUString("rectplus11");
        case BitmapMarkerKind::Crosshair:
            return OUString("cross");
        case BitmapMarkerKind::Anchor:
        case BitmapMarkerKind::AnchorTR:
            return OUString("anchor");
        case BitmapMarkerKind::AnchorPressed:
        case BitmapMarkerKind::AnchorPressedTR:
            return OUString("anchor-pressed");
        case BitmapMarkerKind::Glue:
            return OUString("glue-selected");
        case BitmapMarkerKind::Glue_Deselected:
            return OUString("glue-unselected");
        default:
            break;
    }
    return OUString();
}
 
OUString appendMarkerColor(BitmapColorIndex eIndex)
{
    switch(eIndex)
    {
        case BitmapColorIndex::LightGreen:
            return OUString("1");
        case BitmapColorIndex::Cyan:
            return OUString("2");
        case BitmapColorIndex::LightCyan:
            return OUString("3");
        case BitmapColorIndex::Red:
            return OUString("4");
        case BitmapColorIndex::LightRed:
            return OUString("5");
        case BitmapColorIndex::Yellow:
            return OUString("6");
        default:
            break;
    }
    return OUString();
}
 
BitmapEx ImpGetBitmapEx(BitmapMarkerKind eKindOfMarker, BitmapColorIndex eIndex)
{
    // use this code path only when we use HiDPI (for now)
    if (Application::GetDefaultDevice()->GetDPIScalePercentage() > 100)
    {
        OUString sMarkerPrefix("svx/res/marker-");
 
        OUString sMarkerName = appendMarkerName(eKindOfMarker);
        if (!sMarkerName.isEmpty())
        {
            BitmapEx aBitmapEx;
 
            if (eKindOfMarker == BitmapMarkerKind::Crosshair
             || eKindOfMarker == BitmapMarkerKind::Anchor
             || eKindOfMarker == BitmapMarkerKind::AnchorTR
             || eKindOfMarker == BitmapMarkerKind::AnchorPressed
             || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR
             || eKindOfMarker == BitmapMarkerKind::Glue
             || eKindOfMarker == BitmapMarkerKind::Glue_Deselected)
            {
                aBitmapEx = vcl::bitmap::loadFromName(sMarkerPrefix + sMarkerName + ".png");
            }
            else
            {
                aBitmapEx = vcl::bitmap::loadFromName(sMarkerPrefix + sMarkerName + "-" + appendMarkerColor(eIndex) + ".png");
            }
 
            if (!aBitmapEx.IsEmpty())
                return aBitmapEx;
        }
    }
 
    // if we can't load the marker..
 
    static vcl::DeleteOnDeinit< SdrHdlBitmapSet > aModernSet(new SdrHdlBitmapSet);
    return aModernSet.get()->GetBitmapEx(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(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() && pHdlList && 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::Crosshair:
                    eNextBigger = BitmapMarkerKind::Glue;
                    break;
 
                case BitmapMarkerKind::Glue:
                    eNextBigger = BitmapMarkerKind::Crosshair;
                    break;
                case BitmapMarkerKind::Glue_Deselected:
                    eNextBigger = BitmapMarkerKind::Glue;
                    break;
                default:
                    break;
            }
        }
 
        // create animated handle
        BitmapEx aBmpEx1 = ImpGetBitmapEx(eKindOfMarker, eColIndex);
        BitmapEx aBmpEx2 = ImpGetBitmapEx(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, aBmpEx1, aBmpEx2, 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, aBmpEx1, aBmpEx2, nBlinkTime,
                static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1), 0,
                static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1), 0));
        }
        else
        {
            // create centered handle as default
            pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime,
                static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
                static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
                static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1) >> 1,
                static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Height() - 1) >> 1));
        }
    }
    else
    {
        // create normal handle: use ImpGetBitmapEx(...) now
        BitmapEx aBmpEx = ImpGetBitmapEx(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 (aBmpEx.IsEmpty())
        {
            aBmpEx = BitmapEx(Bitmap(Size(13, 13), 24));
            aBmpEx.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, aBmpEx));
        }
        else if(eKindOfMarker == BitmapMarkerKind::AnchorTR || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR)
        {
            // AnchorTR for SW, take top right as (0,0)
            pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx,
                static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1), 0));
        }
        else
        {
            sal_uInt16 nCenX(static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1) >> 1);
            sal_uInt16 nCenY(static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Height() - 1) >> 1);
 
            if(aMoveOutsideOffset.X() > 0)
            {
                nCenX = 0;
            }
            else if(aMoveOutsideOffset.X() < 0)
            {
                nCenX = static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1);
            }
 
            if(aMoveOutsideOffset.Y() > 0)
            {
                nCenY = 0;
            }
            else if(aMoveOutsideOffset.Y() < 0)
            {
                nCenY = static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Height() - 1);
            }
 
            // create centered handle as default
            pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx, nCenX, nCenY));
        }
    }
 
    return pRetval;
}
 
bool SdrHdl::IsHdlHit(const Point& rPnt) const
{
    // OVERLAYMANAGER
    basegfx::B2DPoint aPosition(rPnt.X(), rPnt.Y());
    return maOverlayGroup.isHitLogic(aPosition);
}
 
Pointer SdrHdl::GetPointer() const
{
    PointerStyle ePtr=PointerStyle::Move;
    const bool bSize=eKind>=SdrHdlKind::UpperLeft && eKind<=SdrHdlKind::LowerRight;
    const bool bRot=pHdlList!=nullptr && pHdlList->IsRotateShear();
    const bool bDis=pHdlList!=nullptr && pHdlList->IsDistortShear();
    if (bSize && pHdlList!=nullptr && (bRot || bDis)) {
        switch (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 && nRotationAngle!=0) {
            long nHdlAngle=0;
            switch (eKind) {
                case SdrHdlKind::LowerRight: nHdlAngle=31500; break;
                case SdrHdlKind::Lower: nHdlAngle=27000; break;
                case SdrHdlKind::LowerLeft: nHdlAngle=22500; break;
                case SdrHdlKind::Left : nHdlAngle=18000; break;
                case SdrHdlKind::UpperLeft: nHdlAngle=13500; break;
                case SdrHdlKind::Upper: nHdlAngle=9000;  break;
                case SdrHdlKind::UpperRight: nHdlAngle=4500;  break;
                case SdrHdlKind::Right: nHdlAngle=0;     break;
                default:
                    break;
            }
            // a little bit more (for rounding)
            nHdlAngle = NormAngle36000(nHdlAngle + nRotationAngle + 2249);
            nHdlAngle/=4500;
            switch (static_cast<sal_uInt8>(nHdlAngle)) {
                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 (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::Hand; break;
                default:
                    break;
            }
        }
    }
    return Pointer(ePtr);
}
 
bool SdrHdl::IsFocusHdl() const
{
    switch(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 !pHdlList || !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:      // glue point
 
        // for SJ and the CustomShapeHandles:
        case SdrHdlKind::CustomShape1:
 
        case SdrHdlKind::User:
        {
            return true;
        }
 
        default:
        {
            return false;
        }
    }
}
 
void SdrHdl::onMouseEnter(const MouseEvent& /*rMEvt*/)
{
}
 
void SdrHdl::onMouseLeave()
{
}
 
BitmapEx SdrHdl::createGluePointBitmap()
{
    return ImpGetBitmapEx(BitmapMarkerKind::Glue_Deselected, BitmapColorIndex::LightGreen);
}
 
SdrHdlColor::SdrHdlColor(const Point& rRef, Color aCol, const Size& rSize, bool bLum)
:   SdrHdl(rRef, SdrHdlKind::Color),
    aMarkerSize(rSize),
    bUseLuminance(bLum)
{
    if(IsUseLuminance())
        aCol = GetLuminance(aCol);
 
    // remember color
    aMarkerColor = aCol;
}
 
SdrHdlColor::~SdrHdlColor()
{
}
 
void SdrHdlColor::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();
 
    if(pHdlList)
    {
        SdrMarkView* pView = pHdlList->GetView();
 
        if(pView && !pView->areMarkHandlesHidden())
        {
            SdrPageView* pPageView = pView->GetSdrPageView();
 
            if(pPageView)
            {
                for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
                {
                    const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
 
                    if(rPageWindow.GetPaintWindow().OutputToWindow())
                    {
                        rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
                        if (xManager.is())
                        {
                            BitmapEx aBmpCol(CreateColorDropper(aMarkerColor));
                            basegfx::B2DPoint aPosition(aPos.X(), 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
                            xManager->add(*pNewOverlayObject);
                            maOverlayGroup.append(std::move(pNewOverlayObject));
                        }
                    }
                }
            }
        }
    }
}
 
BitmapEx SdrHdlColor::CreateColorDropper(Color aCol)
{
    // get the Bitmap
    VclPtr<VirtualDevice> pWrite(VclPtr<VirtualDevice>::Create());
    pWrite->SetOutputSizePixel(aMarkerSize);
    pWrite->SetBackground(aCol);
    pWrite->Erase();
 
    // draw outer border
    sal_Int32 nWidth = aMarkerSize.Width();
    sal_Int32 nHeight = 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->GetBitmapEx(Point(0,0), 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(aMarkerColor != aNew)
    {
        // remember new color
        aMarkerColor = aNew;
 
        // create new display
        Touch();
 
        // tell about change
        if(bCallLink)
            aColorChangeHdl.Call(this);
    }
}
 
void SdrHdlColor::SetSize(const Size& rNew)
{
    if(rNew != aMarkerSize)
    {
        // remember new size
        aMarkerSize = rNew;
 
        // create new display
        Touch();
    }
}
 
SdrHdlGradient::SdrHdlGradient(const Point& rRef1, const Point& rRef2, bool bGrad)
    : SdrHdl(rRef1, bGrad ? SdrHdlKind::Gradient : SdrHdlKind::Transparence)
    , pColHdl1(nullptr)
    , pColHdl2(nullptr)
    , a2ndPos(rRef2)
    , bGradient(bGrad)
    , bMoveSingleHandle(false)
    , bMoveFirstHandle(false)
{
}
 
SdrHdlGradient::~SdrHdlGradient()
{
}
 
void SdrHdlGradient::Set2ndPos(const Point& rPnt)
{
    if(a2ndPos != rPnt)
    {
        // remember new position
        a2ndPos = rPnt;
 
        // create new display
        Touch();
    }
}
 
void SdrHdlGradient::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();
 
    if(pHdlList)
    {
        SdrMarkView* pView = pHdlList->GetView();
 
        if(pView && !pView->areMarkHandlesHidden())
        {
            SdrPageView* pPageView = pView->GetSdrPageView();
 
            if(pPageView)
            {
                for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
                {
                    const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
 
                    if(rPageWindow.GetPaintWindow().OutputToWindow())
                    {
                        rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
                        if (xManager.is())
                        {
                            // striped line in between
                            basegfx::B2DVector aVec(a2ndPos.X() - aPos.X(), a2ndPos.Y() - 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>(aPos.X() + aVec.getX() * fLongPercentArrow);
                            sal_Int32 nMidY = static_cast<sal_Int32>(aPos.Y() + aVec.getY() * fLongPercentArrow);
                            Point aMidPoint(nMidX, nMidY);
 
                            basegfx::B2DPoint aPosition(aPos.X(), 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);
                            xManager->add(*pNewOverlayObject);
                            maOverlayGroup.append(std::move(pNewOverlayObject));
 
                            // 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(a2ndPos.X(), a2ndPos.Y());
 
                            pNewOverlayObject.reset(new
                                sdr::overlay::OverlayTriangle(
                                    aPositionLeft,
                                    aPosition2,
                                    aPositionRight,
                                    IsGradient() ? COL_BLACK : COL_BLUE
                                ));
 
                            xManager->add(*pNewOverlayObject);
                            maOverlayGroup.append(std::move(pNewOverlayObject));
                        }
                    }
                }
            }
        }
    }
}
 
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();
 
    GradTransformer aGradTransformer;
    GradTransGradient aOldGradTransGradient;
    GradTransGradient aGradTransGradient;
    GradTransVector aGradTransVector;
 
    OUString aString;
 
    aGradTransVector.maPositionA = basegfx::B2DPoint(GetPos().X(), GetPos().Y());
    aGradTransVector.maPositionB = basegfx::B2DPoint(Get2ndPos().X(), Get2ndPos().Y());
    if(pColHdl1)
        aGradTransVector.aCol1 = pColHdl1->GetColor();
    if(pColHdl2)
        aGradTransVector.aCol2 = 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, bMoveSingleHandle, bMoveFirstHandle);
 
    if(bSetItemOnObject)
    {
        SdrModel& rModel(_pObj->getSdrModelFromSdrObject());
        SfxItemSet aNewSet(rModel.GetItemPool());
 
        if(IsGradient())
        {
            aString.clear();
            XFillGradientItem aNewGradItem(aString, aGradTransGradient.aGradient);
            aNewSet.Put(aNewGradItem);
        }
        else
        {
            aString.clear();
            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();
        }
 
        pObj->SetMergedItemSetAndBroadcast(aNewSet);
    }
 
    // back transformation, set values on pIAOHandle
    GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, _pObj);
 
    SetPos(Point(FRound(aGradTransVector.maPositionA.getX()), FRound(aGradTransVector.maPositionA.getY())));
    Set2ndPos(Point(FRound(aGradTransVector.maPositionB.getX()), FRound(aGradTransVector.maPositionB.getY())));
    if(pColHdl1)
    {
        pColHdl1->SetPos(Point(FRound(aGradTransVector.maPositionA.getX()), FRound(aGradTransVector.maPositionA.getY())));
        pColHdl1->SetColor(aGradTransVector.aCol1);
    }
    if(pColHdl2)
    {
        pColHdl2->SetPos(Point(FRound(aGradTransVector.maPositionB.getX()), FRound(aGradTransVector.maPositionB.getY())));
        pColHdl2->SetColor(aGradTransVector.aCol2);
    }
}
 
 
SdrHdlLine::~SdrHdlLine() {}
 
void SdrHdlLine::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();
 
    if(pHdlList)
    {
        SdrMarkView* pView = pHdlList->GetView();
 
        if(pView && !pView->areMarkHandlesHidden() && pHdl1 && pHdl2)
        {
            SdrPageView* pPageView = pView->GetSdrPageView();
 
            if(pPageView)
            {
                for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
                {
                    const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
 
                    if(rPageWindow.GetPaintWindow().OutputToWindow())
                    {
                        rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
                        if (xManager.is())
                        {
                            basegfx::B2DPoint aPosition1(pHdl1->GetPos().X(), pHdl1->GetPos().Y());
                            basegfx::B2DPoint aPosition2(pHdl2->GetPos().X(), pHdl2->GetPos().Y());
 
                            std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
                                sdr::overlay::OverlayLineStriped(
                                    aPosition1,
                                    aPosition2
                                ));
 
                            // OVERLAYMANAGER
                            // color(?)
                            pNewOverlayObject->setBaseColor(COL_LIGHTRED);
 
                            xManager->add(*pNewOverlayObject);
                            maOverlayGroup.append(std::move(pNewOverlayObject));
                        }
                    }
                }
            }
        }
    }
}
 
Pointer SdrHdlLine::GetPointer() const
{
    return Pointer(PointerStyle::RefHand);
}
 
 
SdrHdlBezWgt::~SdrHdlBezWgt() {}
 
void SdrHdlBezWgt::CreateB2dIAObject()
{
    // call parent
    SdrHdl::CreateB2dIAObject();
 
    // create lines
    if(pHdlList)
    {
        SdrMarkView* pView = pHdlList->GetView();
 
        if(pView && !pView->areMarkHandlesHidden())
        {
            SdrPageView* pPageView = pView->GetSdrPageView();
 
            if(pPageView)
            {
                for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
                {
                    const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
 
                    if(rPageWindow.GetPaintWindow().OutputToWindow())
                    {
                        rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
                        if (xManager.is())
                        {
                            basegfx::B2DPoint aPosition1(pHdl1->GetPos().X(), pHdl1->GetPos().Y());
                            basegfx::B2DPoint aPosition2(aPos.X(), aPos.Y());
 
                            if(!aPosition1.equal(aPosition2))
                            {
                                std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
                                    sdr::overlay::OverlayLineStriped(
                                        aPosition1,
                                        aPosition2
                                    ));
                                // OVERLAYMANAGER
                                // line part is not hittable
                                pNewOverlayObject->setHittable(false);
 
                                // color(?)
                                pNewOverlayObject->setBaseColor(COL_LIGHTBLUE);
 
                                xManager->add(*pNewOverlayObject);
                                maOverlayGroup.append(std::move(pNewOverlayObject));
                            }
                        }
                    }
                }
            }
        }
    }
}
 
 
E3dVolumeMarker::E3dVolumeMarker(const basegfx::B2DPolyPolygon& rWireframePoly)
{
    aWireframePoly = rWireframePoly;
}
 
void E3dVolumeMarker::CreateB2dIAObject()
{
    // create lines
    if(pHdlList)
    {
        SdrMarkView* pView = pHdlList->GetView();
 
        if(pView && !pView->areMarkHandlesHidden())
        {
            SdrPageView* pPageView = pView->GetSdrPageView();
 
            if(pPageView)
            {
                for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
                {
                    const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
 
                    if(rPageWindow.GetPaintWindow().OutputToWindow())
                    {
                        rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
                        if (xManager.is() && aWireframePoly.count())
                        {
                            std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
                                sdr::overlay::OverlayPolyPolygonStripedAndFilled(
                                    aWireframePoly));
 
                            // OVERLAYMANAGER
                            pNewOverlayObject->setBaseColor(COL_BLACK);
 
                            xManager->add(*pNewOverlayObject);
                            maOverlayGroup.append(std::move(pNewOverlayObject));
                        }
                    }
                }
            }
        }
    }
}
 
 
ImpEdgeHdl::~ImpEdgeHdl()
{
}
 
void ImpEdgeHdl::CreateB2dIAObject()
{
    if(nObjHdlNum <= 1 && pObj)
    {
        // first throw away old one
        GetRidOfIAObject();
 
        BitmapColorIndex eColIndex = BitmapColorIndex::LightCyan;
        BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_7x7;
 
        if(pHdlList)
        {
            SdrMarkView* pView = pHdlList->GetView();
 
            if(pView && !pView->areMarkHandlesHidden())
            {
                const SdrEdgeObj* pEdge = static_cast<SdrEdgeObj*>(pObj);
 
                if(pEdge->GetConnectedNode(nObjHdlNum == 0) != nullptr)
                    eColIndex = BitmapColorIndex::LightRed;
 
                if(nPPntNum < 2)
                {
                    // Handle with plus sign inside
                    eKindOfMarker = BitmapMarkerKind::Circ_7x7;
                }
 
                SdrPageView* pPageView = pView->GetSdrPageView();
 
                if(pPageView)
                {
                    for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
                    {
                        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
 
                        if(rPageWindow.GetPaintWindow().OutputToWindow())
                        {
                            rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
                            if (xManager.is())
                            {
                                basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
                                std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(CreateOverlayObject(
                                    aPosition,
                                    eColIndex,
                                    eKindOfMarker));
 
                                // OVERLAYMANAGER
                                if (pNewOverlayObject)
                                {
                                    xManager->add(*pNewOverlayObject);
                                    maOverlayGroup.append(std::move(pNewOverlayObject));
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    else
    {
        // call parent
        SdrHdl::CreateB2dIAObject();
    }
}
 
void ImpEdgeHdl::SetLineCode(SdrEdgeLineCode eCode)
{
    if(eLineCode != eCode)
    {
        // remember new value
        eLineCode = eCode;
 
        // create new display
        Touch();
    }
}
 
Pointer ImpEdgeHdl::GetPointer() const
{
    SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( pObj );
    if (pEdge==nullptr)
        return SdrHdl::GetPointer();
    if (nObjHdlNum<=1)
        return Pointer(PointerStyle::MovePoint);
    if (IsHorzDrag())
        return Pointer(PointerStyle::ESize);
    else
        return Pointer(PointerStyle::SSize);
}
 
bool ImpEdgeHdl::IsHorzDrag() const
{
    SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( pObj );
    if (pEdge==nullptr)
        return false;
    if (nObjHdlNum<=1)
        return false;
 
    SdrEdgeKind eEdgeKind = pEdge->GetObjectItem(SDRATTR_EDGEKIND).GetValue();
 
    const SdrEdgeInfoRec& rInfo=pEdge->aEdgeInfo;
    if (eEdgeKind==SdrEdgeKind::OrthoLines || eEdgeKind==SdrEdgeKind::Bezier)
    {
        return !rInfo.ImpIsHorzLine(eLineCode,*pEdge->pEdgeTrack);
    }
    else if (eEdgeKind==SdrEdgeKind::ThreeLines)
    {
        long nAngle=nObjHdlNum==2 ? rInfo.nAngle1 : rInfo.nAngle2;
        return nAngle==0 || nAngle==18000;
    }
    return false;
}
 
 
ImpMeasureHdl::~ImpMeasureHdl()
{
}
 
void ImpMeasureHdl::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();
 
    if(pHdlList)
    {
        SdrMarkView* pView = pHdlList->GetView();
 
        if(pView && !pView->areMarkHandlesHidden())
        {
            BitmapColorIndex eColIndex = BitmapColorIndex::LightCyan;
            BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_9x9;
 
            if(nObjHdlNum > 1)
            {
                eKindOfMarker = BitmapMarkerKind::Rect_7x7;
            }
 
            if(bSelect)
            {
                eColIndex = BitmapColorIndex::Cyan;
            }
 
            SdrPageView* pPageView = pView->GetSdrPageView();
 
            if(pPageView)
            {
                for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
                {
                    const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
 
                    if(rPageWindow.GetPaintWindow().OutputToWindow())
                    {
                        rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
                        if (xManager.is())
                        {
                            basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
                            std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(CreateOverlayObject(
                                aPosition,
                                eColIndex,
                                eKindOfMarker));
 
                            // OVERLAYMANAGER
                            if (pNewOverlayObject)
                            {
                                xManager->add(*pNewOverlayObject);
                                maOverlayGroup.append(std::move(pNewOverlayObject));
                            }
                        }
                    }
                }
            }
        }
    }
}
 
Pointer ImpMeasureHdl::GetPointer() const
{
    switch (nObjHdlNum)
    {
        case 0: case 1: return Pointer(PointerStyle::Hand);
        case 2: case 3: return Pointer(PointerStyle::MovePoint);
        case 4: case 5: return SdrHdl::GetPointer(); // will then be rotated appropriately
    } // switch
    return Pointer(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(pHdlList)
    {
        SdrMarkView* pView = pHdlList->GetView();
 
        if(pView && !pView->areMarkHandlesHidden())
        {
            SdrPageView* pPageView = pView->GetSdrPageView();
 
            if(pPageView)
            {
                for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
                {
                    const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
 
                    if(rPageWindow.GetPaintWindow().OutputToWindow())
                    {
                        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 SvtOptionsDrawinglayer aSvtOptionsDrawinglayer;
                            const Color aHilightColor(aSvtOptionsDrawinglayer.getHilightColor());
                            const double fTransparence(aSvtOptionsDrawinglayer.GetTransparentSelectionPercent() * 0.01);
 
                            std::unique_ptr<sdr::overlay::OverlayRectangle> pNewOverlayObject(new sdr::overlay::OverlayRectangle(
                                aTopLeft,
                                aBottomRight,
                                aHilightColor,
                                fTransparence,
                                3.0,
                                3.0,
                                nRotationAngle * -F_PI18000,
                                true)); // allow animation; the Handle is not shown at text edit time
 
                            // OVERLAYMANAGER
                            pNewOverlayObject->setHittable(false);
                            xManager->add(*pNewOverlayObject);
                            maOverlayGroup.append(std::move(pNewOverlayObject));
                        }
                    }
                }
            }
        }
    }
}
 
 
static bool ImpSdrHdlListSorter(SdrHdl* const& lhs, 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;
    }
}
 
 
// Helper struct for re-sorting handles
struct ImplHdlAndIndex
{
    SdrHdl*                     mpHdl;
    sal_uInt32                  mnIndex;
};
 
// Helper method for sorting handles taking care of OrdNums, keeping order in
// single objects and re-sorting polygon handles intuitively
extern "C" 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(aList.empty())
        return;
 
    // take care of old handle
    const size_t nOldHdlNum(mnFocusIndex);
    SdrHdl* 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[aList.size()]);
 
    // build sorted handle list
    for( size_t a = 0; a < aList.size(); ++a)
    {
        pHdlAndIndex[a].mpHdl = aList[a];
        pHdlAndIndex[a].mnIndex = a;
    }
 
    qsort(pHdlAndIndex.get(), aList.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 < aList.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 == aList.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 = aList.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 < aList.size(); ++a)
        {
            if(aList[a] == pNew)
            {
                nNewHdlNum = a;
                break;
            }
        }
    }
 
    // take care of next handle
    if(nOldHdlNum != nNewHdlNum)
    {
        mnFocusIndex = nNewHdlNum;
        SdrHdl* pNew = GetHdl(mnFocusIndex);
 
        if(pNew)
        {
            pNew->Touch();
        }
    }
}
 
SdrHdl* SdrHdlList::GetFocusHdl() const
{
    if(mnFocusIndex < GetHdlCount())
        return GetHdl(mnFocusIndex);
    else
        return nullptr;
}
 
void SdrHdlList::SetFocusHdl(SdrHdl* pNew)
{
    if(pNew)
    {
        SdrHdl* pActual = GetFocusHdl();
 
        if(!pActual || pActual != pNew)
        {
            const size_t nNewHdlNum = GetHdlNum(pNew);
 
            if(nNewHdlNum != SAL_MAX_SIZE)
            {
                mnFocusIndex = nNewHdlNum;
 
                if(pActual)
                {
                    pActual->Touch();
                }
 
                if(pNew)
                {
                    pNew->Touch();
                }
 
            }
        }
    }
}
 
void SdrHdlList::ResetFocusHdl()
{
    SdrHdl* pHdl = GetFocusHdl();
 
    mnFocusIndex = SAL_MAX_SIZE;
 
    if(pHdl)
    {
        pHdl->Touch();
    }
}
 
 
SdrHdlList::SdrHdlList(SdrMarkView* pV)
:   mnFocusIndex(SAL_MAX_SIZE),
    pView(pV),
    aList()
{
    nHdlSize = 3;
    bRotateShear = false;
    bMoveOutside = false;
    bDistortShear = false;
}
 
SdrHdlList::~SdrHdlList()
{
    Clear();
}
 
void SdrHdlList::SetHdlSize(sal_uInt16 nSiz)
{
    if(nHdlSize != nSiz)
    {
        // remember new value
        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(bMoveOutside != bOn)
    {
        // remember new value
        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)
{
    bRotateShear = bOn;
}
 
void SdrHdlList::SetDistortShear(bool bOn)
{
    bDistortShear = bOn;
}
 
SdrHdl* SdrHdlList::RemoveHdl(size_t nNum)
{
    SdrHdl* pRetval = aList[nNum];
    aList.erase(aList.begin() + nNum);
 
    return pRetval;
}
 
void SdrHdlList::RemoveAllByKind(SdrHdlKind eKind)
{
    for(std::deque<SdrHdl*>::iterator it = aList.begin(); it != aList.end(); )
    {
        SdrHdl* p = *it;
        if (p->GetKind() == eKind)
        {
            it = aList.erase( it );
            delete p;
        }
        else
            ++it;
    }
}
 
void SdrHdlList::Clear()
{
    for (size_t i=0; i<GetHdlCount(); ++i)
    {
        SdrHdl* pHdl=GetHdl(i);
        delete pHdl;
    }
    aList.clear();
 
    bRotateShear=false;
    bDistortShear=false;
}
 
void SdrHdlList::Sort()
{
    // remember currently focused handle
    SdrHdl* pPrev = GetFocusHdl();
 
    std::sort( aList.begin(), aList.end(), ImpSdrHdlListSorter );
 
    // get now and compare
    SdrHdl* pNow = GetFocusHdl();
 
    if(pPrev != pNow)
    {
 
        if(pPrev)
        {
            pPrev->Touch();
        }
 
        if(pNow)
        {
            pNow->Touch();
        }
    }
}
 
size_t SdrHdlList::GetHdlNum(const SdrHdl* pHdl) const
{
    if (pHdl==nullptr)
        return SAL_MAX_SIZE;
    std::deque<SdrHdl*>::const_iterator it = std::find( aList.begin(), aList.end(), pHdl);
    if( it == aList.end() )
        return SAL_MAX_SIZE;
    return it - aList.begin();
}
 
void SdrHdlList::AddHdl(SdrHdl* pHdl)
{
    if (pHdl!=nullptr)
    {
        aList.push_back(pHdl);
        pHdl->SetHdlList(this);
    }
}
 
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;
}
 
SdrCropHdl::SdrCropHdl(
    const Point& rPnt,
    SdrHdlKind eNewKind,
    double fShearX,
    double fRotation)
:   SdrHdl(rPnt, eNewKind),
    mfShearX(fShearX),
    mfRotation(fRotation)
{
}
 
 
BitmapEx SdrCropHdl::GetBitmapForHandle( const BitmapEx& 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( 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) );
 
    BitmapEx aRetval(rBitmap);
    aRetval.Crop(aSourceRect);
    return aRetval;
}
 
 
void SdrCropHdl::CreateB2dIAObject()
{
    // first throw away old one
    GetRidOfIAObject();
 
    SdrMarkView* pView = pHdlList ? pHdlList->GetView() : nullptr;
    SdrPageView* pPageView = pView ? pView->GetSdrPageView() : nullptr;
 
    if( pPageView && !pView->areMarkHandlesHidden() )
    {
        const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
        int nHdlSize = pHdlList->GetHdlSize();
 
        const BitmapEx aHandlesBitmap(SIP_SA_CROP_MARKERS);
        BitmapEx aBmpEx1( GetBitmapForHandle( aHandlesBitmap, nHdlSize ) );
 
        for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
        {
            const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
 
            if(rPageWindow.GetPaintWindow().OutputToWindow())
            {
                rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
                if (xManager.is())
                {
                    basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
 
                    std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject;
 
                    // animate focused handles
                    if(IsFocusHdl() && (pHdlList->GetFocusHdl() == this))
                    {
                        if( nHdlSize >= 2 )
                            nHdlSize = 1;
 
                        BitmapEx aBmpEx2( GetBitmapForHandle( aHandlesBitmap, nHdlSize + 1 ) );
 
                        const sal_uInt64 nBlinkTime = rStyleSettings.GetCursorBlinkTime();
 
                        pOverlayObject.reset(new sdr::overlay::OverlayAnimatedBitmapEx(
                            aPosition,
                            aBmpEx1,
                            aBmpEx2,
                            nBlinkTime,
                            static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
                            static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
                            static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1) >> 1,
                            static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Height() - 1) >> 1,
                            mfShearX,
                            mfRotation));
                    }
                    else
                    {
                        // create centered handle as default
                        pOverlayObject.reset(new sdr::overlay::OverlayBitmapEx(
                            aPosition,
                            aBmpEx1,
                            static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
                            static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
                            0.0,
                            mfShearX,
                            mfRotation));
                    }
 
                    // OVERLAYMANAGER
                    if(pOverlayObject)
                    {
                        xManager->add(*pOverlayObject);
                        maOverlayGroup.append(std::move(pOverlayObject));
                    }
                }
            }
        }
    }
}
 
 
// with the correction of crop handling I could get rid of the extra mirroring flag, adapted stuff
// accordingly
 
SdrCropViewHdl::SdrCropViewHdl(
    const basegfx::B2DHomMatrix& rObjectTransform,
    const Graphic& rGraphic,
    double fCropLeft,
    double fCropTop,
    double fCropRight,
    double fCropBottom)
:   SdrHdl(Point(), SdrHdlKind::User),
    maObjectTransform(rObjectTransform),
    maGraphic(rGraphic),
    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), F_PI, 0.000000001))
    {
        scale.setX(scale.getX() * -1.0);
        scale.setY(scale.getY() * -1.0);
        *rotate = 0.0;
    }
}
 
}
 
void SdrCropViewHdl::CreateB2dIAObject()
{
    GetRidOfIAObject();
    SdrMarkView* pView = pHdlList ? 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 SvtOptionsDrawinglayer aSvtOptionsDrawinglayer;
    const basegfx::BColor aHilightColor(aSvtOptionsDrawinglayer.getHilightColor().getBColor());
    const drawinglayer::primitive2d::Primitive2DReference aGraphicOutline(
        new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
        aGraphicOutlinePolygon,
        aHilightColor));
 
    // combine these
    drawinglayer::primitive2d::Primitive2DContainer aCombination(2);
    aCombination[0] = aGraphic;
    aCombination[1] = aGraphicOutline;
 
    // embed to MaskPrimitive2D
    const drawinglayer::primitive2d::Primitive2DReference aMaskedGraphic(
        new drawinglayer::primitive2d::MaskPrimitive2D(
            aCropPolyPolygon,
            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())
        {
            rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
            if(xManager.is())
            {
                std::unique_ptr<sdr::overlay::OverlayObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(aSequence));
 
                // only informative object, no hit
                pNew->setHittable(false);
 
                xManager->add(*pNew);
                maOverlayGroup.append(std::move(pNew));
            }
        }
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V547 Expression 'pNew' is always true.

V668 There is no sense in testing the 'pOverlayObject' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.