    /* -*- 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 <svx/svdobj.hxx>
    #include <config_features.h>

    #include <sal/config.h>
    #include <sal/log.hxx>

    #include <com/sun/star/lang/XComponent.hpp>
    #include <com/sun/star/text/RelOrientation.hpp>

    #include <basegfx/matrix/b2dhommatrix.hxx>
    #include <basegfx/matrix/b2dhommatrixtools.hxx>
    #include <basegfx/polygon/b2dpolygon.hxx>
    #include <basegfx/polygon/b2dpolygontools.hxx>
    #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
    #include <basegfx/polygon/b2dpolypolygontools.hxx>
    #include <basegfx/range/b2drange.hxx>
    #include <drawinglayer/processor2d/contourextractor2d.hxx>
    #include <drawinglayer/processor2d/linegeometryextractor2d.hxx>
    #include <editeng/editeng.hxx>
    #include <editeng/eeitem.hxx>
    #include <editeng/outlobj.hxx>
    #include <o3tl/deleter.hxx>
    #include <math.h>
    #include <sfx2/objface.hxx>
    #include <sfx2/objsh.hxx>
    #include <svl/whiter.hxx>
    #include <svl/grabbagitem.hxx>
    #include <svtools/colorcfg.hxx>
    #include <tools/bigint.hxx>
    #include <tools/diagnose_ex.h>
    #include <tools/helpers.hxx>
    #include <tools/line.hxx>
    #include <unotools/configmgr.hxx>
    #include <vcl/graphictools.hxx>
    #include <vcl/metaact.hxx>
    #include <vcl/virdev.hxx>
    #include <vector>

    #include <svx/shapepropertynotifier.hxx>
    #include <svx/svdotable.hxx>
    #include <svx/xlinjoit.hxx>

    #include <svx/fmmodel.hxx>
    #include <svx/sdr/contact/displayinfo.hxx>
    #include <svx/sdr/contact/objectcontactofobjlistpainter.hxx>
    #include <sdr/contact/viewcontactofgraphic.hxx>
    #include <svx/sdr/contact/viewcontactofsdrobj.hxx>
    #include <sdr/properties/emptyproperties.hxx>
    #include <svx/sdrhittesthelper.hxx>
    #include <svx/sdrobjectuser.hxx>
    #include <svx/sdrobjectfilter.hxx>
    #include <svx/svddrag.hxx>
    #include <svx/svdetc.hxx>
    #include <svx/svdhdl.hxx>
    #include <svx/svditer.hxx>
    #include <svx/svdmodel.hxx>
    #include <svx/svdoashp.hxx>
    #include <svx/svdocapt.hxx>
    #include <svx/svdocirc.hxx>
    #include <svx/svdoedge.hxx>
    #include <svx/svdograf.hxx>
    #include <svx/svdogrp.hxx>
    #include <svx/svdomeas.hxx>
    #include <svx/svdomedia.hxx>
    #include <svx/svdoole2.hxx>
    #include <svx/svdopage.hxx>
    #include <svx/svdopath.hxx>
    #include <svx/svdorect.hxx>
    #include <svx/svdotext.hxx>
    #include <svx/svdouno.hxx>
    #include <svx/svdovirt.hxx>
    #include <svx/svdpage.hxx>
    #include <svx/svdpool.hxx>
    #include <svx/strings.hrc>
    #include <svx/dialmgr.hxx>
    #include <svx/svdtrans.hxx>
    #include <svx/svdundo.hxx>
    #include <svx/svdview.hxx>
    #include <sxlayitm.hxx>
    #include <sxlogitm.hxx>
    #include <sxmovitm.hxx>
    #include <sxoneitm.hxx>
    #include <sxopitm.hxx>
    #include <sxreoitm.hxx>
    #include <sxrooitm.hxx>
    #include <sxsaitm.hxx>
    #include <sxsoitm.hxx>
    #include <sxtraitm.hxx>
    #include <svx/unopage.hxx>
    #include <svx/unoshape.hxx>
    #include <svx/xbtmpit.hxx>
    #include <svx/xenum.hxx>
    #include <svx/xflclit.hxx>
    #include <svx/xflftrit.hxx>
    #include <svx/xflhtit.hxx>
    #include <svx/xfltrit.hxx>
    #include <svx/xgrad.hxx>
    #include <svx/xhatch.hxx>
    #include <svx/xlnclit.hxx>
    #include <svx/xlndsit.hxx>
    #include <svx/xlnedcit.hxx>
    #include <svx/xlnedit.hxx>
    #include <svx/xlnedwit.hxx>
    #include <svx/xlnstcit.hxx>
    #include <svx/xlnstit.hxx>
    #include <svx/xlnstwit.hxx>
    #include <svx/xlntrit.hxx>
    #include <svx/xlnwtit.hxx>
    #include <svx/xpoly.hxx>
    #include <svx/svdglue.hxx>
    #include <svx/svdsob.hxx>
    #include <rtl/strbuf.hxx>
    #include <svdobjplusdata.hxx>
    #include <svdobjuserdatalist.hxx>
    #include <o3tl/make_unique.hxx>

    #include <boost/optional.hpp>
    #include <libxml/xmlwriter.h>
    #include <memory>

    using namespace ::com::sun::star;

    SdrObjUserCall::~SdrObjUserCall()
    {
    }

    void SdrObjUserCall::Changed(const SdrObject& /*rObj*/, SdrUserCallType /*eType*/, const tools::Rectangle& /*rOldBoundRect*/)
    {
    }

    SdrObjMacroHitRec::SdrObjMacroHitRec() :
        pOut(nullptr),
        pVisiLayer(nullptr),
        pPageView(nullptr),
        nTol(0) {}

    SdrObjUserData::SdrObjUserData(SdrInventor nInv, sal_uInt16 nId) :
        nInventor(nInv),
        nIdentifier(nId) {}

    SdrObjUserData::SdrObjUserData(const SdrObjUserData& rData) :
        nInventor(rData.nInventor),
        nIdentifier(rData.nIdentifier) {}

    SdrObjUserData::~SdrObjUserData() {}

    SdrObjGeoData::SdrObjGeoData():
        bMovProt(false),
        bSizProt(false),
        bNoPrint(false),
        bClosedObj(false),
        mbVisible(true),
        mnLayerID(0)
    {
    }

    SdrObjGeoData::~SdrObjGeoData()
    {
    }

    SdrObjTransformInfoRec::SdrObjTransformInfoRec() :
        bMoveAllowed(true),
        bResizeFreeAllowed(true),
        bResizePropAllowed(true),
        bRotateFreeAllowed(true),
        bRotate90Allowed(true),
        bMirrorFreeAllowed(true),
        bMirror45Allowed(true),
        bMirror90Allowed(true),
        bTransparenceAllowed(true),
        bShearAllowed(true),
        bEdgeRadiusAllowed(true),
        bNoOrthoDesired(true),
        bNoContortion(true),
        bCanConvToPath(true),
        bCanConvToPoly(true),
        bCanConvToContour(false),
        bCanConvToPathLineToArea(true),
        bCanConvToPolyLineToArea(true) {}

    struct SdrObject::Impl
    {
        sdr::ObjectUserVector maObjectUsers;

        boost::optional<double> mnRelativeWidth;
        sal_Int16               meRelativeWidthRelation;
        boost::optional<double> mnRelativeHeight;
        sal_Int16               meRelativeHeightRelation;

        Impl() :
            meRelativeWidthRelation(text::RelOrientation::PAGE_FRAME),
            meRelativeHeightRelation(text::RelOrientation::PAGE_FRAME) {}
    };

    // BaseProperties section

    std::unique_ptr<sdr::properties::BaseProperties> SdrObject::CreateObjectSpecificProperties()
    {
        return o3tl::make_unique<sdr::properties::EmptyProperties>(*this);
    }

    sdr::properties::BaseProperties& SdrObject::GetProperties() const
    {
        if(!mpProperties)
        {
            // CAUTION(!) Do *not* call this during SdrObject construction,
            // that will lead to wrong type-casts (dependent on constructor-level)
            // and thus eventually create the wrong sdr::properties (!). Is there
            // a way to check if on the stack is a SdrObject-constructor (?)
            const_cast< SdrObject* >(this)->mpProperties =
                const_cast< SdrObject* >(this)->CreateObjectSpecificProperties();
        }

        return *mpProperties;
    }

    // ObjectUser section

    void SdrObject::AddObjectUser(sdr::ObjectUser& rNewUser)
    {
        mpImpl->maObjectUsers.push_back(&rNewUser);
    }

    void SdrObject::RemoveObjectUser(sdr::ObjectUser& rOldUser)
    {
        const sdr::ObjectUserVector::iterator aFindResult =
            std::find(mpImpl->maObjectUsers.begin(), mpImpl->maObjectUsers.end(), &rOldUser);
        if (aFindResult != mpImpl->maObjectUsers.end())
        {
            mpImpl->maObjectUsers.erase(aFindResult);
        }
    }

    // DrawContact section

    std::unique_ptr<sdr::contact::ViewContact> SdrObject::CreateObjectSpecificViewContact()
    {
        return o3tl::make_unique<sdr::contact::ViewContactOfSdrObj>(*this);
    }

    sdr::contact::ViewContact& SdrObject::GetViewContact() const
    {
        if(!mpViewContact)
        {
            const_cast< SdrObject* >(this)->mpViewContact =
                const_cast< SdrObject* >(this)->CreateObjectSpecificViewContact();
        }

        return *mpViewContact;
    }

    // DrawContact support: Methods for handling Object changes
    void SdrObject::ActionChanged() const
    {
        // Do necessary ViewContact actions
        GetViewContact().ActionChanged();
    }

    SdrPage* SdrObject::getSdrPageFromSdrObject() const
    {
        if(getParentSdrObjListFromSdrObject())
        {
            return getParentSdrObjListFromSdrObject()->getSdrPageFromSdrObjList();
        }

        return nullptr;
    }

    SdrModel& SdrObject::getSdrModelFromSdrObject() const
    {
        return mrSdrModelFromSdrObject;
    }

    void SdrObject::setParentOfSdrObject(SdrObjList* pNewObjList)
    {
        if(getParentSdrObjListFromSdrObject() != pNewObjList)
        {
            // remember current page
            SdrPage* pOldPage(getSdrPageFromSdrObject());

            // set new parent
            mpParentOfSdrObject = pNewObjList;

            // get new page
            SdrPage* pNewPage(getSdrPageFromSdrObject());

            // broadcast page change over objects if needed
            if(pOldPage != pNewPage)
            {
                handlePageChange(pOldPage, pNewPage);
            }
        }
    }

    SdrObjList* SdrObject::getParentSdrObjListFromSdrObject() const
    {
        return mpParentOfSdrObject;
    }

    SdrObjList* SdrObject::getChildrenOfSdrObject() const
    {
        // default has no children
        return nullptr;
    }

    void SdrObject::SetBoundRectDirty()
    {
        aOutRect = tools::Rectangle();
    }

    #ifdef DBG_UTIL
    // SdrObjectLifetimeWatchDog:
    void impAddIncarnatedSdrObjectToSdrModel(const SdrObject& rSdrObject, SdrModel& rSdrModel)
    {
        rSdrModel.maAllIncarnatedObjects.insert(&rSdrObject);
    }
    void impRemoveIncarnatedSdrObjectToSdrModel(const SdrObject& rSdrObject, SdrModel& rSdrModel)
    {
        if(!rSdrModel.maAllIncarnatedObjects.erase(&rSdrObject))
        {
            SAL_WARN("svx","SdrObject::~SdrObject: Destructed incarnation of SdrObject not member of this SdrModel (!)");
        }
    }
    #endif

    SdrObject::SdrObject(SdrModel& rSdrModel)
    :   mpFillGeometryDefiningShape(nullptr)
        ,mrSdrModelFromSdrObject(rSdrModel)
        ,pUserCall(nullptr)
        ,mpImpl(new Impl)
        ,mpParentOfSdrObject(nullptr)
        ,nOrdNum(0)
        ,mnNavigationPosition(SAL_MAX_UINT32)
        ,mnLayerID(0)
        ,mpSvxShape( nullptr )
        ,maWeakUnoShape()
        ,mbDoNotInsertIntoPageAutomatically(false)
    {
        bVirtObj         =false;
        bSnapRectDirty   =true;
        bMovProt         =false;
        bSizProt         =false;
        bNoPrint         =false;
        bEmptyPresObj    =false;
        bNotVisibleAsMaster=false;
        bClosedObj       =false;
        mbVisible        = true;

        // #i25616#
        mbLineIsOutsideGeometry = false;

        // #i25616#
        mbSupportTextIndentingOnLineWidthChange = false;

        bIsEdge=false;
        bIs3DObj=false;
        bMarkProt=false;
        bIsUnoObj=false;
    #ifdef DBG_UTIL
        // SdrObjectLifetimeWatchDog:
        impAddIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject());
    #endif
    }

    SdrObject::~SdrObject()
    {
        // tell all the registered ObjectUsers that the page is in destruction
        sdr::ObjectUserVector aListCopy(mpImpl->maObjectUsers.begin(), mpImpl->maObjectUsers.end());
        for(sdr::ObjectUserVector::iterator aIterator = aListCopy.begin(); aIterator != aListCopy.end(); ++aIterator)
        {
            sdr::ObjectUser* pObjectUser = *aIterator;
            DBG_ASSERT(pObjectUser, "SdrObject::~SdrObject: corrupt ObjectUser list (!)");
            pObjectUser->ObjectInDestruction(*this);
        }

        // Clear the vector. This means that user do not need to call RemoveObjectUser()
        // when they get called from ObjectInDestruction().
        mpImpl->maObjectUsers.clear();

        // UserCall
        SendUserCall(SdrUserCallType::Delete, GetLastBoundRect());
        o3tl::reset_preserve_ptr_during(pPlusData);

        pGrabBagItem.reset();
        mpProperties.reset();
        mpViewContact.reset();

    #ifdef DBG_UTIL
        // SdrObjectLifetimeWatchDog:
        impRemoveIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject());
    #endif
    }

    void SdrObject::Free( SdrObject*& _rpObject )
    {
        SdrObject* pObject = _rpObject; _rpObject = nullptr;

        if(nullptr == pObject)
        {
            // nothing to do
            return;
        }

        SvxShape* pShape(pObject->getSvxShape());

        if(pShape)
        {
            if(pShape->HasSdrObjectOwnership())
            {
                // only the SvxShape is allowed to delete me, and will reset
                // the ownership before doing so
                return;
            }
            else
            {
                // not only delete pObject, but also need to dispose uno shape
                try
                {
                    pShape->InvalidateSdrObject();
                    uno::Reference< lang::XComponent > xShapeComp( pObject->getWeakUnoShape(), uno::UNO_QUERY_THROW );
                    xShapeComp->dispose();
                }
                catch( const uno::Exception& )
                {
                    DBG_UNHANDLED_EXCEPTION("svx");
                }
            }
        }

        delete pObject;
    }

    void SdrObject::SetRectsDirty(bool bNotMyself, bool bRecursive)
    {
        if (!bNotMyself)
        {
            SetBoundRectDirty();
            bSnapRectDirty=true;
        }

        if (bRecursive && nullptr != getParentSdrObjListFromSdrObject())
        {
            getParentSdrObjListFromSdrObject()->SetSdrObjListRectsDirty();
        }
    }

    void SdrObject::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
    {
        // The creation of the UNO shape in SdrObject::getUnoShape is influenced
        // by pPage, so when the page changes we need to discard the cached UNO
        // shape so that a new one will be created.
        // If the page is changing to another page with the same model, we
        // assume they create compatible UNO shape objects so we shouldn't have
        // to invalidate.
        // TTTT: This causes quite some problems in SvxDrawPage::add when used
        // e.g. from Writer - the SdrObject may be cloned to target model, and
        // the xShape was added to it by purpose (see there). Thus it will be
        // good to think about if this is really needed - it *seems* to be intended
        // for a xShape being a on-demand-creatable resource - with the argument that
        // the SdrPage/UnoPage used influences the SvxShape creation. This uses
        // resources and would be nice to get rid of anyways.
        if(nullptr == pOldPage || nullptr == pNewPage)
        {
            SvxShape* const pShape(getSvxShape());

            if (pShape && !pShape->HasSdrObjectOwnership())
            {
                setUnoShape(nullptr);
            }
        }
    }

    // init global static itempool
    SdrItemPool* SdrObject::mpGlobalItemPool = nullptr;

    SdrItemPool& SdrObject::GetGlobalDrawObjectItemPool()
    {
        if(!mpGlobalItemPool)
        {
            mpGlobalItemPool = new SdrItemPool();
            SfxItemPool* pGlobalOutlPool = EditEngine::CreatePool();
            mpGlobalItemPool->SetSecondaryPool(pGlobalOutlPool);
            mpGlobalItemPool->SetDefaultMetric(SdrEngineDefaults::GetMapUnit());
            mpGlobalItemPool->FreezeIdRanges();
        }

        return *mpGlobalItemPool;
    }

    void SdrObject::SetRelativeWidth( double nValue )
    {
        mpImpl->mnRelativeWidth.reset( nValue );
    }

    void SdrObject::SetRelativeWidthRelation( sal_Int16 eValue )
    {
        mpImpl->meRelativeWidthRelation = eValue;
    }

    void SdrObject::SetRelativeHeight( double nValue )
    {
        mpImpl->mnRelativeHeight.reset( nValue );
    }

    void SdrObject::SetRelativeHeightRelation( sal_Int16 eValue )
    {
        mpImpl->meRelativeHeightRelation = eValue;
    }

    const double* SdrObject::GetRelativeWidth( ) const
    {
        if (!mpImpl->mnRelativeWidth)
            return nullptr;

        return &mpImpl->mnRelativeWidth.get();
    }

    sal_Int16 SdrObject::GetRelativeWidthRelation() const
    {
        return mpImpl->meRelativeWidthRelation;
    }

    const double* SdrObject::GetRelativeHeight( ) const
    {
        if (!mpImpl->mnRelativeHeight)
            return nullptr;

        return &mpImpl->mnRelativeHeight.get();
    }

    sal_Int16 SdrObject::GetRelativeHeightRelation() const
    {
        return mpImpl->meRelativeHeightRelation;
    }

    SfxItemPool& SdrObject::GetObjectItemPool() const
    {
        return getSdrModelFromSdrObject().GetItemPool();
    }

    SdrInventor SdrObject::GetObjInventor()   const
    {
        return SdrInventor::Default;
    }

    sal_uInt16 SdrObject::GetObjIdentifier() const
    {
        return sal_uInt16(OBJ_NONE);
    }

    void SdrObject::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
    {
        rInfo.bRotateFreeAllowed=false;
        rInfo.bMirrorFreeAllowed=false;
        rInfo.bTransparenceAllowed = false;
        rInfo.bShearAllowed     =false;
        rInfo.bEdgeRadiusAllowed=false;
        rInfo.bCanConvToPath    =false;
        rInfo.bCanConvToPoly    =false;
        rInfo.bCanConvToContour = false;
        rInfo.bCanConvToPathLineToArea=false;
        rInfo.bCanConvToPolyLineToArea=false;
    }

    SdrLayerID SdrObject::GetLayer() const
    {
        return mnLayerID;
    }

    void SdrObject::getMergedHierarchySdrLayerIDSet(SdrLayerIDSet& rSet) const
    {
        rSet.Set(GetLayer());
        SdrObjList* pOL=GetSubList();
        if (pOL!=nullptr) {
            const size_t nObjCount = pOL->GetObjCount();
            for (size_t nObjNum = 0; nObjNum<nObjCount; ++nObjNum) {
                pOL->GetObj(nObjNum)->getMergedHierarchySdrLayerIDSet(rSet);
            }
        }
    }

    void SdrObject::NbcSetLayer(SdrLayerID nLayer)
    {
        mnLayerID = nLayer;
    }

    void SdrObject::SetLayer(SdrLayerID nLayer)
    {
        NbcSetLayer(nLayer);
        SetChanged();
        BroadcastObjectChange();
    }

    void SdrObject::AddListener(SfxListener& rListener)
    {
        ImpForcePlusData();
        if (pPlusData->pBroadcast==nullptr) pPlusData->pBroadcast.reset(new SfxBroadcaster);

        // SdrEdgeObj may be connected to same SdrObject on both ends so allow it
        // to listen twice
        SdrEdgeObj const*const pEdge(dynamic_cast<SdrEdgeObj const*>(&rListener));
        rListener.StartListening(*pPlusData->pBroadcast, pEdge ? DuplicateHandling::Allow : DuplicateHandling::Unexpected);
    }

    void SdrObject::RemoveListener(SfxListener& rListener)
    {
        if (pPlusData!=nullptr && pPlusData->pBroadcast!=nullptr) {
            rListener.EndListening(*pPlusData->pBroadcast);
            if (!pPlusData->pBroadcast->HasListeners()) {
                pPlusData->pBroadcast.reset();
            }
        }
    }

    const SfxBroadcaster* SdrObject::GetBroadcaster() const
    {
        return pPlusData!=nullptr ? pPlusData->pBroadcast.get() : nullptr;
    }

    void SdrObject::AddReference(SdrVirtObj& rVrtObj)
    {
        AddListener(rVrtObj);
    }

    void SdrObject::DelReference(SdrVirtObj& rVrtObj)
    {
        RemoveListener(rVrtObj);
    }

    bool SdrObject::IsGroupObject() const
    {
        return GetSubList()!=nullptr;
    }

    SdrObjList* SdrObject::GetSubList() const
    {
        return nullptr;
    }

    SdrObject* SdrObject::getParentSdrObjectFromSdrObject() const
    {
        SdrObjList* pParent(getParentSdrObjListFromSdrObject());

        if(nullptr == pParent)
        {
            return nullptr;
        }

        return pParent->getSdrObjectFromSdrObjList();
    }

    void SdrObject::SetName(const OUString& rStr)
    {
        if (!rStr.isEmpty() && !pPlusData)
        {
            ImpForcePlusData();
        }

        if(pPlusData && pPlusData->aObjName != rStr)
        {
            // Undo/Redo for setting object's name (#i73249#)
            bool bUndo( false );
            if ( getSdrModelFromSdrObject().IsUndoEnabled() )
            {
                bUndo = true;
                SdrUndoAction* pUndoAction =
                        SdrUndoFactory::CreateUndoObjectStrAttr(
                                                        *this,
                                                        SdrUndoObjStrAttr::ObjStrAttrType::Name,
                                                        GetName(),
                                                        rStr );
                getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() );
                getSdrModelFromSdrObject().AddUndo( pUndoAction );
            }
            pPlusData->aObjName = rStr;
            // Undo/Redo for setting object's name (#i73249#)
            if ( bUndo )
            {
                getSdrModelFromSdrObject().EndUndo();
            }
            SetChanged();
            BroadcastObjectChange();
        }
    }

    OUString SdrObject::GetName() const
    {
        if(pPlusData)
        {
            return pPlusData->aObjName;
        }

        return OUString();
    }

    void SdrObject::SetTitle(const OUString& rStr)
    {
        if (!rStr.isEmpty() && !pPlusData)
        {
            ImpForcePlusData();
        }

        if(pPlusData && pPlusData->aObjTitle != rStr)
        {
            // Undo/Redo for setting object's title (#i73249#)
            bool bUndo( false );
            if ( getSdrModelFromSdrObject().IsUndoEnabled() )
            {
                bUndo = true;
                SdrUndoAction* pUndoAction =
                        SdrUndoFactory::CreateUndoObjectStrAttr(
                                                        *this,
                                                        SdrUndoObjStrAttr::ObjStrAttrType::Title,
                                                        GetTitle(),
                                                        rStr );
                getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() );
                getSdrModelFromSdrObject().AddUndo( pUndoAction );
            }
            pPlusData->aObjTitle = rStr;
            // Undo/Redo for setting object's title (#i73249#)
            if ( bUndo )
            {
                getSdrModelFromSdrObject().EndUndo();
            }
            SetChanged();
            BroadcastObjectChange();
        }
    }

    OUString SdrObject::GetTitle() const
    {
        if(pPlusData)
        {
            return pPlusData->aObjTitle;
        }

        return OUString();
    }

    void SdrObject::SetDescription(const OUString& rStr)
    {
        if (!rStr.isEmpty() && !pPlusData)
        {
            ImpForcePlusData();
        }

        if(pPlusData && pPlusData->aObjDescription != rStr)
        {
            // Undo/Redo for setting object's description (#i73249#)
            bool bUndo( false );
            if ( getSdrModelFromSdrObject().IsUndoEnabled() )
            {
                bUndo = true;
                SdrUndoAction* pUndoAction =
                        SdrUndoFactory::CreateUndoObjectStrAttr(
                                                        *this,
                                                        SdrUndoObjStrAttr::ObjStrAttrType::Description,
                                                        GetDescription(),
                                                        rStr );
                getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() );
                getSdrModelFromSdrObject().AddUndo( pUndoAction );
            }
            pPlusData->aObjDescription = rStr;
            // Undo/Redo for setting object's description (#i73249#)
            if ( bUndo )
            {
                getSdrModelFromSdrObject().EndUndo();
            }
            SetChanged();
            BroadcastObjectChange();
        }
    }

    OUString SdrObject::GetDescription() const
    {
        if(pPlusData)
        {
            return pPlusData->aObjDescription;
        }

        return OUString();
    }

    sal_uInt32 SdrObject::GetOrdNum() const
    {
        if (nullptr != getParentSdrObjListFromSdrObject())
        {
            if (getParentSdrObjListFromSdrObject()->IsObjOrdNumsDirty())
            {
                getParentSdrObjListFromSdrObject()->RecalcObjOrdNums();
            }
        } else const_cast<SdrObject*>(this)->nOrdNum=0;
        return nOrdNum;
    }

    void SdrObject::SetOrdNum(sal_uInt32 nNum)
    {
        nOrdNum = nNum;
    }

    void SdrObject::GetGrabBagItem(css::uno::Any& rVal) const
    {
        if (pGrabBagItem != nullptr)
            pGrabBagItem->QueryValue(rVal);
        else
            rVal <<= uno::Sequence<beans::PropertyValue>();
    }

    void SdrObject::SetGrabBagItem(const css::uno::Any& rVal)
    {
        if (pGrabBagItem == nullptr)
            pGrabBagItem.reset(new SfxGrabBagItem);

        pGrabBagItem->PutValue(rVal, 0);

        SetChanged();
        BroadcastObjectChange();
    }

    sal_uInt32 SdrObject::GetNavigationPosition()
    {
        if (nullptr != getParentSdrObjListFromSdrObject() && getParentSdrObjListFromSdrObject()->RecalcNavigationPositions())
        {
            return mnNavigationPosition;
        }
        else
            return GetOrdNum();
    }

    void SdrObject::SetNavigationPosition (const sal_uInt32 nNewPosition)
    {
        mnNavigationPosition = nNewPosition;
    }

    // To make clearer that this method may trigger RecalcBoundRect and thus may be
    // expensive and sometimes problematic (inside a bigger object change you will get
    // non-useful BoundRects sometimes) I rename that method from GetBoundRect() to
    // GetCurrentBoundRect().
    const tools::Rectangle& SdrObject::GetCurrentBoundRect() const
    {
        if(aOutRect.IsEmpty())
        {
            const_cast< SdrObject* >(this)->RecalcBoundRect();
        }

        return aOutRect;
    }

    // To have a possibility to get the last calculated BoundRect e.g for producing
    // the first rectangle for repaints (old and new need to be used) without forcing
    // a RecalcBoundRect (which may be problematical and expensive sometimes) I add here
    // a new method for accessing the last BoundRect.
    const tools::Rectangle& SdrObject::GetLastBoundRect() const
    {
        return aOutRect;
    }

    void SdrObject::RecalcBoundRect()
    {
        // #i101680# suppress BoundRect calculations on import(s)
        if ((getSdrModelFromSdrObject().isLocked()) || utl::ConfigManager::IsFuzzing())
            return;

        // central new method which will calculate the BoundRect using primitive geometry
        if(aOutRect.IsEmpty())
        {
            const drawinglayer::primitive2d::Primitive2DContainer xPrimitives(GetViewContact().getViewIndependentPrimitive2DContainer());

            if(!xPrimitives.empty())
            {
                // use neutral ViewInformation and get the range of the primitives
                const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
                const basegfx::B2DRange aRange(xPrimitives.getB2DRange(aViewInformation2D));

                if(!aRange.isEmpty())
                {
                    aOutRect = tools::Rectangle(
                        static_cast<long>(floor(aRange.getMinX())),
                        static_cast<long>(floor(aRange.getMinY())),
                        static_cast<long>(ceil(aRange.getMaxX())),
                        static_cast<long>(ceil(aRange.getMaxY())));
                    aOutRect -= GetGridOffset(); // don't include grid offset
                    return;
                }
            }
        }
    }

    void SdrObject::BroadcastObjectChange() const
    {
        if ((getSdrModelFromSdrObject().isLocked()) || utl::ConfigManager::IsFuzzing())
            return;

        bool bPlusDataBroadcast(pPlusData && pPlusData->pBroadcast);
        bool bObjectChange(IsInserted());

        if(bPlusDataBroadcast || bObjectChange)
        {
            SdrHint aHint(SdrHintKind::ObjectChange, *this);

            if(bPlusDataBroadcast)
            {
                pPlusData->pBroadcast->Broadcast(aHint);
            }

            if(bObjectChange)
            {
                getSdrModelFromSdrObject().Broadcast(aHint);
            }
        }
    }

    void SdrObject::SetChanged()
    {
        // For testing purposes, use the new ViewContact for change
        // notification now.
        ActionChanged();

        // TTTT Need to check meaning/usage of IsInserted in one
        // of the next changes. It should not mean to have a SdrModel
        // set (this is guaranteed now), but should be connected to
        // being added to a SdrPage (?)
        if(IsInserted())
        {
            getSdrModelFromSdrObject().SetChanged();
        }
    }

    // tooling for painting a single object to an OutputDevice.
    void SdrObject::SingleObjectPainter(OutputDevice& rOut) const
    {
        sdr::contact::SdrObjectVector aObjectVector;
        aObjectVector.push_back(const_cast< SdrObject* >(this));

        sdr::contact::ObjectContactOfObjListPainter aPainter(rOut, aObjectVector, getSdrPageFromSdrObject());
        sdr::contact::DisplayInfo aDisplayInfo;

        aPainter.ProcessDisplay(aDisplayInfo);
    }

    bool SdrObject::LineGeometryUsageIsNecessary() const
    {
        drawing::LineStyle eXLS = GetMergedItem(XATTR_LINESTYLE).GetValue();
        return (eXLS != drawing::LineStyle_NONE);
    }

    bool SdrObject::HasLimitedRotation() const
    {
        // RotGrfFlyFrame: Default is false, support full rotation
        return false;
    }

    SdrObject* SdrObject::CloneSdrObject(SdrModel& rTargetModel) const
    {
        return CloneHelper< SdrObject >(rTargetModel);
    }

    SdrObject& SdrObject::operator=(const SdrObject& rObj)
    {
        if( this == &rObj )
            return *this;

        mpProperties.reset();
        mpViewContact.reset();

        // The CloneSdrObject() method uses the local copy constructor from the individual
        // sdr::properties::BaseProperties class. Since the target class maybe for another
        // draw object, an SdrObject needs to be provided, as in the normal constructor.
        mpProperties = rObj.GetProperties().Clone(*this);

        aOutRect=rObj.aOutRect;
        mnLayerID = rObj.mnLayerID;
        aAnchor =rObj.aAnchor;
        bVirtObj=rObj.bVirtObj;
        bSizProt=rObj.bSizProt;
        bMovProt=rObj.bMovProt;
        bNoPrint=rObj.bNoPrint;
        mbVisible=rObj.mbVisible;
        bMarkProt=rObj.bMarkProt;
        bEmptyPresObj =rObj.bEmptyPresObj;
        bNotVisibleAsMaster=rObj.bNotVisibleAsMaster;
        bSnapRectDirty=true;
        pPlusData.reset();
        if (rObj.pPlusData!=nullptr) {
            pPlusData.reset(rObj.pPlusData->Clone(this));
        }
        if (pPlusData!=nullptr && pPlusData->pBroadcast!=nullptr) {
            pPlusData->pBroadcast.reset(); // broadcaster isn't copied
        }

        pGrabBagItem.reset();
        if (rObj.pGrabBagItem!=nullptr)
            pGrabBagItem.reset(static_cast< SfxGrabBagItem* >( rObj.pGrabBagItem->Clone() ));

        aGridOffset = rObj.aGridOffset;
        return *this;
    }

    OUString SdrObject::TakeObjNameSingul() const
    {
        OUStringBuffer sName(SvxResId(STR_ObjNameSingulNONE));

        OUString aName(GetName());
        if (!aName.isEmpty())
        {
            sName.append(' ');
            sName.append('\'');
            sName.append(aName);
            sName.append('\'');
        }
        return sName.makeStringAndClear();
    }

    OUString SdrObject::TakeObjNamePlural() const
    {
        return SvxResId(STR_ObjNamePluralNONE);
    }

    void SdrObject::ImpTakeDescriptionStr(const char* pStrCacheID, OUString& rStr) const
    {
        rStr = SvxResId(pStrCacheID);
        sal_Int32 nPos = rStr.indexOf("%1");
        if (nPos >= 0)
        {
            // Replace '%1' with the object name.
            OUString aObjName(TakeObjNameSingul());
            rStr = rStr.replaceAt(nPos, 2, aObjName);
        }

        nPos = rStr.indexOf("%2");
        if (nPos >= 0)
            // Replace '%2' with the passed value.
            rStr = rStr.replaceAt(nPos, 2, "0");
    }

    void SdrObject::ImpForcePlusData()
    {
        if (!pPlusData)
            pPlusData.reset( new SdrObjPlusData );
    }

    OUString SdrObject::GetMetrStr(long nVal) const
    {
        return getSdrModelFromSdrObject().GetMetricString(nVal);
    }

    basegfx::B2DPolyPolygon SdrObject::TakeXorPoly() const
    {
        basegfx::B2DPolyPolygon aRetval;
        const tools::Rectangle aR(GetCurrentBoundRect());
        const basegfx::B2DRange aRange(aR.Left(), aR.Top(), aR.Right(), aR.Bottom());
        aRetval.append(basegfx::utils::createPolygonFromRect(aRange));

        return aRetval;
    }

    basegfx::B2DPolyPolygon SdrObject::TakeContour() const
    {
        basegfx::B2DPolyPolygon aRetval;

        // create cloned object without text, but with drawing::LineStyle_SOLID,
        // COL_BLACK as line color and drawing::FillStyle_NONE
        SdrObject* pClone(CloneSdrObject(getSdrModelFromSdrObject()));

        if(pClone)
        {
            const SdrTextObj* pTextObj = dynamic_cast< const SdrTextObj* >(this);

            if(pTextObj)
            {
                // no text and no text animation
                pClone->SetMergedItem(SdrTextAniKindItem(SdrTextAniKind::NONE));
                pClone->SetOutlinerParaObject(nullptr);
            }

            const SdrEdgeObj* pEdgeObj = dynamic_cast< const SdrEdgeObj* >(this);

            if(pEdgeObj)
            {
                // create connections if connector, will be cleaned up when
                // deleting the connector again
                SdrObject* pLeft = pEdgeObj->GetConnectedNode(true);
                SdrObject* pRight = pEdgeObj->GetConnectedNode(false);

                if(pLeft)
                {
                    pClone->ConnectToNode(true, pLeft);
                }

                if(pRight)
                {
                    pClone->ConnectToNode(false, pRight);
                }
            }

            SfxItemSet aNewSet(GetObjectItemPool());

            // #i101980# ignore LineWidth; that's what the old implementation
            // did. With line width, the result may be huge due to fat/thick
            // line decompositions
            aNewSet.Put(XLineWidthItem(0));

            // solid black lines and no fill
            aNewSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
            aNewSet.Put(XLineColorItem(OUString(), COL_BLACK));
            aNewSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
            pClone->SetMergedItemSet(aNewSet);

            // get sequence from clone
            const sdr::contact::ViewContact& rVC(pClone->GetViewContact());
            const drawinglayer::primitive2d::Primitive2DContainer xSequence(rVC.getViewIndependentPrimitive2DContainer());

            if(!xSequence.empty())
            {
                // use neutral ViewInformation
                const drawinglayer::geometry::ViewInformation2D aViewInformation2D;

                // create extractor, process and get result (with hairlines as opened polygons)
                drawinglayer::processor2d::ContourExtractor2D aExtractor(aViewInformation2D, false);
                aExtractor.process(xSequence);
                const basegfx::B2DPolyPolygonVector& rResult(aExtractor.getExtractedContour());
                const sal_uInt32 nSize(rResult.size());

                // when count is one, it is implied that the object has only its normal
                // contour anyways and TakeContour() is to return an empty PolyPolygon
                // (see old implementation for historical reasons)
                if(nSize > 1)
                {
                    // the topology for contour is correctly a vector of PolyPolygons; for
                    // historical reasons cut it back to a single tools::PolyPolygon here
                    for(sal_uInt32 a(0); a < nSize; a++)
                    {
                        aRetval.append(rResult[a]);
                    }
                }
            }

            // Always use SdrObject::Free to delete SdrObjects (!)
            SdrObject::Free(pClone);
        }

        return aRetval;
    }

    sal_uInt32 SdrObject::GetHdlCount() const
    {
        return 8L;
    }

    SdrHdl* SdrObject::GetHdl(sal_uInt32 nHdlNum) const
    {
        SdrHdl* pH=nullptr;
        const tools::Rectangle& rR=GetSnapRect();
        switch (nHdlNum) {
            case 0: pH=new SdrHdl(rR.TopLeft(),     SdrHdlKind::UpperLeft); break;
            case 1: pH=new SdrHdl(rR.TopCenter(),   SdrHdlKind::Upper); break;
            case 2: pH=new SdrHdl(rR.TopRight(),    SdrHdlKind::UpperRight); break;
            case 3: pH=new SdrHdl(rR.LeftCenter(),  SdrHdlKind::Left ); break;
            case 4: pH=new SdrHdl(rR.RightCenter(), SdrHdlKind::Right); break;
            case 5: pH=new SdrHdl(rR.BottomLeft(),  SdrHdlKind::LowerLeft); break;
            case 6: pH=new SdrHdl(rR.BottomCenter(),SdrHdlKind::Lower); break;
            case 7: pH=new SdrHdl(rR.BottomRight(), SdrHdlKind::LowerRight); break;
        }
        return pH;
    }

    sal_uInt32 SdrObject::GetPlusHdlCount(const SdrHdl& /*rHdl*/) const
    {
        return 0L;
    }

    SdrHdl* SdrObject::GetPlusHdl(const SdrHdl& /*rHdl*/, sal_uInt32 /*nPlNum*/) const
    {
        return nullptr;
    }

    void SdrObject::AddToHdlList(SdrHdlList& rHdlList) const
    {
        sal_uInt32 nCount=GetHdlCount();
        for (sal_uInt32 i=0; i<nCount; i++) {
            SdrHdl* pHdl=GetHdl(i);
            if (pHdl!=nullptr) {
                rHdlList.AddHdl(pHdl);
            }
        }
    }

    void SdrObject::addCropHandles(SdrHdlList& /*rTarget*/) const
    {
        // Default implementation, does nothing. Overloaded in
        // SdrGrafObj and SwVirtFlyDrawObj
    }

    tools::Rectangle SdrObject::ImpDragCalcRect(const SdrDragStat& rDrag) const
    {
        tools::Rectangle aTmpRect(GetSnapRect());
        tools::Rectangle aRect(aTmpRect);
        const SdrHdl* pHdl=rDrag.GetHdl();
        SdrHdlKind eHdl=pHdl==nullptr ? SdrHdlKind::Move : pHdl->GetKind();
        bool bEcke=(eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::LowerLeft || eHdl==SdrHdlKind::LowerRight);
        bool bOrtho=rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho();
        bool bBigOrtho=bEcke && bOrtho && rDrag.GetView()->IsBigOrtho();
        Point aPos(rDrag.GetNow());
        bool bLft=(eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::Left  || eHdl==SdrHdlKind::LowerLeft);
        bool bRgt=(eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::Right || eHdl==SdrHdlKind::LowerRight);
        bool bTop=(eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::Upper || eHdl==SdrHdlKind::UpperLeft);
        bool bBtm=(eHdl==SdrHdlKind::LowerRight || eHdl==SdrHdlKind::Lower || eHdl==SdrHdlKind::LowerLeft);
        if (bLft) aTmpRect.SetLeft(aPos.X() );
        if (bRgt) aTmpRect.SetRight(aPos.X() );
        if (bTop) aTmpRect.SetTop(aPos.Y() );
        if (bBtm) aTmpRect.SetBottom(aPos.Y() );
        if (bOrtho) { // Ortho
            long nWdt0=aRect.Right() -aRect.Left();
            long nHgt0=aRect.Bottom()-aRect.Top();
            long nXMul=aTmpRect.Right() -aTmpRect.Left();
            long nYMul=aTmpRect.Bottom()-aTmpRect.Top();
            long nXDiv=nWdt0;
            long nYDiv=nHgt0;
            bool bXNeg=(nXMul<0)!=(nXDiv<0);
            bool bYNeg=(nYMul<0)!=(nYDiv<0);
            nXMul=std::abs(nXMul);
            nYMul=std::abs(nYMul);
            nXDiv=std::abs(nXDiv);
            nYDiv=std::abs(nYDiv);
            Fraction aXFact(nXMul,nXDiv); // fractions for canceling
            Fraction aYFact(nYMul,nYDiv); // and for comparing
            nXMul=aXFact.GetNumerator();
            nYMul=aYFact.GetNumerator();
            nXDiv=aXFact.GetDenominator();
            nYDiv=aYFact.GetDenominator();
            if (bEcke) { // corner point handles
                bool bUseX=(aXFact<aYFact) != bBigOrtho;
                if (bUseX) {
                    long nNeed=long(BigInt(nHgt0)*BigInt(nXMul)/BigInt(nXDiv));
                    if (bYNeg) nNeed=-nNeed;
                    if (bTop) aTmpRect.SetTop(aTmpRect.Bottom()-nNeed );
                    if (bBtm) aTmpRect.SetBottom(aTmpRect.Top()+nNeed );
                } else {
                    long nNeed=long(BigInt(nWdt0)*BigInt(nYMul)/BigInt(nYDiv));
                    if (bXNeg) nNeed=-nNeed;
                    if (bLft) aTmpRect.SetLeft(aTmpRect.Right()-nNeed );
                    if (bRgt) aTmpRect.SetRight(aTmpRect.Left()+nNeed );
                }
            } else { // apex handles
                if ((bLft || bRgt) && nXDiv!=0) {
                    long nHgt0b=aRect.Bottom()-aRect.Top();
                    long nNeed=long(BigInt(nHgt0b)*BigInt(nXMul)/BigInt(nXDiv));
                    aTmpRect.AdjustTop( -((nNeed-nHgt0b)/2) );
                    aTmpRect.SetBottom(aTmpRect.Top()+nNeed );
                }
                if ((bTop || bBtm) && nYDiv!=0) {
                    long nWdt0b=aRect.Right()-aRect.Left();
                    long nNeed=long(BigInt(nWdt0b)*BigInt(nYMul)/BigInt(nYDiv));
                    aTmpRect.AdjustLeft( -((nNeed-nWdt0b)/2) );
                    aTmpRect.SetRight(aTmpRect.Left()+nNeed );
                }
            }
        }
        aTmpRect.Justify();
        return aTmpRect;
    }

    bool SdrObject::hasSpecialDrag() const
    {
        return false;
    }

    bool SdrObject::supportsFullDrag() const
    {
        return true;
    }

    SdrObject* SdrObject::getFullDragClone() const
    {
        // default uses simple clone
        return CloneSdrObject(getSdrModelFromSdrObject());
    }

    bool SdrObject::beginSpecialDrag(SdrDragStat& rDrag) const
    {
        const SdrHdl* pHdl = rDrag.GetHdl();

        SdrHdlKind eHdl = (pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind();

        return eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::Upper || eHdl==SdrHdlKind::UpperRight ||
            eHdl==SdrHdlKind::Left || eHdl==SdrHdlKind::Right || eHdl==SdrHdlKind::LowerLeft ||
            eHdl==SdrHdlKind::Lower || eHdl==SdrHdlKind::LowerRight;
    }

    bool SdrObject::applySpecialDrag(SdrDragStat& rDrag)
    {
        tools::Rectangle aNewRect(ImpDragCalcRect(rDrag));

        if(aNewRect != GetSnapRect())
        {
               NbcSetSnapRect(aNewRect);
        }

        return true;
    }

    OUString SdrObject::getSpecialDragComment(const SdrDragStat& /*rDrag*/) const
    {
        return OUString();
    }

    basegfx::B2DPolyPolygon SdrObject::getSpecialDragPoly(const SdrDragStat& /*rDrag*/) const
    {
        // default has nothing to add
        return basegfx::B2DPolyPolygon();
    }

    // Create
    bool SdrObject::BegCreate(SdrDragStat& rStat)
    {
        rStat.SetOrtho4Possible();
        tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
        aRect1.Justify();
        rStat.SetActionRect(aRect1);
        aOutRect = aRect1;
        return true;
    }

    bool SdrObject::MovCreate(SdrDragStat& rStat)
    {
        rStat.TakeCreateRect(aOutRect);
        rStat.SetActionRect(aOutRect);
        aOutRect.Justify();

        return true;
    }

    bool SdrObject::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
    {
        rStat.TakeCreateRect(aOutRect);
        aOutRect.Justify();

        return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
    }

    void SdrObject::BrkCreate(SdrDragStat& /*rStat*/)
    {
    }

    bool SdrObject::BckCreate(SdrDragStat& /*rStat*/)
    {
        return false;
    }

    basegfx::B2DPolyPolygon SdrObject::TakeCreatePoly(const SdrDragStat& rDrag) const
    {
        tools::Rectangle aRect1;
        rDrag.TakeCreateRect(aRect1);
        aRect1.Justify();

        basegfx::B2DPolyPolygon aRetval;
        const basegfx::B2DRange aRange(aRect1.Left(), aRect1.Top(), aRect1.Right(), aRect1.Bottom());
        aRetval.append(basegfx::utils::createPolygonFromRect(aRange));
        return aRetval;
    }

    Pointer SdrObject::GetCreatePointer() const
    {
        return Pointer(PointerStyle::Cross);
    }

    // transformations
    void SdrObject::NbcMove(const Size& rSiz)
    {
        aOutRect.Move(rSiz);
        SetRectsDirty();
    }

    void SdrObject::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
    {
        bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
        bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
        if (bXMirr || bYMirr) {
            Point aRef1(GetSnapRect().Center());
            if (bXMirr) {
                Point aRef2(aRef1);
                aRef2.AdjustY( 1 );
                NbcMirrorGluePoints(aRef1,aRef2);
            }
            if (bYMirr) {
                Point aRef2(aRef1);
                aRef2.AdjustX( 1 );
                NbcMirrorGluePoints(aRef1,aRef2);
            }
        }
        ResizeRect(aOutRect,rRef,xFact,yFact);
        SetRectsDirty();
    }

    void SdrObject::NbcRotate(const Point& rRef, long nAngle, double sn, double cs)
    {
        SetGlueReallyAbsolute(true);
        aOutRect.Move(-rRef.X(),-rRef.Y());
        tools::Rectangle R(aOutRect);
        if (sn==1.0 && cs==0.0) { // 90deg
            aOutRect.SetLeft(-R.Bottom() );
            aOutRect.SetRight(-R.Top() );
            aOutRect.SetTop(R.Left() );
            aOutRect.SetBottom(R.Right() );
        } else if (sn==0.0 && cs==-1.0) { // 180deg
            aOutRect.SetLeft(-R.Right() );
            aOutRect.SetRight(-R.Left() );
            aOutRect.SetTop(-R.Bottom() );
            aOutRect.SetBottom(-R.Top() );
        } else if (sn==-1.0 && cs==0.0) { // 270deg
            aOutRect.SetLeft(R.Top() );
            aOutRect.SetRight(R.Bottom() );
            aOutRect.SetTop(-R.Right() );
            aOutRect.SetBottom(-R.Left() );
        }
        aOutRect.Move(rRef.X(),rRef.Y());
        aOutRect.Justify(); // just in case
        SetRectsDirty();
        NbcRotateGluePoints(rRef,nAngle,sn,cs);
        SetGlueReallyAbsolute(false);
    }

    void SdrObject::NbcMirror(const Point& rRef1, const Point& rRef2)
    {
        SetGlueReallyAbsolute(true);
        aOutRect.Move(-rRef1.X(),-rRef1.Y());
        tools::Rectangle R(aOutRect);
        long dx=rRef2.X()-rRef1.X();
        long dy=rRef2.Y()-rRef1.Y();
        if (dx==0) {          // vertical axis
            aOutRect.SetLeft(-R.Right() );
            aOutRect.SetRight(-R.Left() );
        } else if (dy==0) {   // horizontal axis
            aOutRect.SetTop(-R.Bottom() );
            aOutRect.SetBottom(-R.Top() );
        } else if (dx==dy) {  // 45deg axis
            aOutRect.SetLeft(R.Top() );
            aOutRect.SetRight(R.Bottom() );
            aOutRect.SetTop(R.Left() );
            aOutRect.SetBottom(R.Right() );
        } else if (dx==-dy) { // 45deg axis
            aOutRect.SetLeft(-R.Bottom() );
            aOutRect.SetRight(-R.Top() );
            aOutRect.SetTop(-R.Right() );
            aOutRect.SetBottom(-R.Left() );
        }
        aOutRect.Move(rRef1.X(),rRef1.Y());
        aOutRect.Justify(); // just in case
        SetRectsDirty();
        NbcMirrorGluePoints(rRef1,rRef2);
        SetGlueReallyAbsolute(false);
    }

    void SdrObject::NbcShear(const Point& rRef, long /*nAngle*/, double tn, bool bVShear)
    {
        SetGlueReallyAbsolute(true);
        NbcShearGluePoints(rRef,tn,bVShear);
        SetGlueReallyAbsolute(false);
    }

    void SdrObject::Move(const Size& rSiz)
    {
        if (rSiz.Width()!=0 || rSiz.Height()!=0) {
            tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
            NbcMove(rSiz);
            SetChanged();
            BroadcastObjectChange();
            SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
        }
    }

    void SdrObject::NbcCrop(const basegfx::B2DPoint& /*aRef*/, double /*fxFact*/, double /*fyFact*/)
    {
        // Default: does nothing. Real behaviour in SwVirtFlyDrawObj and SdrDragCrop::EndSdrDrag.
        // Where SwVirtFlyDrawObj is the only real user of it to do something local
    }

    void SdrObject::Resize(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bUnsetRelative)
    {
        if (xFact.GetNumerator()!=xFact.GetDenominator() || yFact.GetNumerator()!=yFact.GetDenominator()) {
            if (bUnsetRelative)
            {
                mpImpl->mnRelativeWidth.reset();
                mpImpl->meRelativeWidthRelation = text::RelOrientation::PAGE_FRAME;
                mpImpl->meRelativeHeightRelation = text::RelOrientation::PAGE_FRAME;
                mpImpl->mnRelativeHeight.reset();
            }
            tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
            NbcResize(rRef,xFact,yFact);
            SetChanged();
            BroadcastObjectChange();
            SendUserCall(SdrUserCallType::Resize,aBoundRect0);
        }
    }

    void SdrObject::Crop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact)
    {
        tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
        NbcCrop(rRef, fxFact, fyFact);
        SetChanged();
        BroadcastObjectChange();
        SendUserCall(SdrUserCallType::Resize,aBoundRect0);
    }

    void SdrObject::Rotate(const Point& rRef, long nAngle, double sn, double cs)
    {
        if (nAngle!=0) {
            tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
            NbcRotate(rRef,nAngle,sn,cs);
            SetChanged();
            BroadcastObjectChange();
            SendUserCall(SdrUserCallType::Resize,aBoundRect0);
        }
    }

    void SdrObject::Mirror(const Point& rRef1, const Point& rRef2)
    {
        tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
        NbcMirror(rRef1,rRef2);
        SetChanged();
        BroadcastObjectChange();
        SendUserCall(SdrUserCallType::Resize,aBoundRect0);
    }

    void SdrObject::Shear(const Point& rRef, long nAngle, double tn, bool bVShear)
    {
        if (nAngle!=0) {
            tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
            NbcShear(rRef,nAngle,tn,bVShear);
            SetChanged();
            BroadcastObjectChange();
            SendUserCall(SdrUserCallType::Resize,aBoundRect0);
        }
    }

    void SdrObject::NbcSetRelativePos(const Point& rPnt)
    {
        Point aRelPos0(GetSnapRect().TopLeft()-aAnchor);
        Size aSiz(rPnt.X()-aRelPos0.X(),rPnt.Y()-aRelPos0.Y());
        NbcMove(aSiz); // This also calls SetRectsDirty()
    }

    void SdrObject::SetRelativePos(const Point& rPnt)
    {
        if (rPnt!=GetRelativePos()) {
            tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
            NbcSetRelativePos(rPnt);
            SetChanged();
            BroadcastObjectChange();
            SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
        }
    }

    Point SdrObject::GetRelativePos() const
    {
        return GetSnapRect().TopLeft()-aAnchor;
    }

    void SdrObject::ImpSetAnchorPos(const Point& rPnt)
    {
        aAnchor = rPnt;
    }

    void SdrObject::NbcSetAnchorPos(const Point& rPnt)
    {
        Size aSiz(rPnt.X()-aAnchor.X(),rPnt.Y()-aAnchor.Y());
        aAnchor=rPnt;
        NbcMove(aSiz); // This also calls SetRectsDirty()
    }

    void SdrObject::SetAnchorPos(const Point& rPnt)
    {
        if (rPnt!=aAnchor) {
            tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
            NbcSetAnchorPos(rPnt);
            SetChanged();
            BroadcastObjectChange();
            SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
        }
    }

    const Point& SdrObject::GetAnchorPos() const
    {
        return aAnchor;
    }

    void SdrObject::RecalcSnapRect()
    {
    }

    const tools::Rectangle& SdrObject::GetSnapRect() const
    {
        return aOutRect;
    }

    void SdrObject::NbcSetSnapRect(const tools::Rectangle& rRect)
    {
        aOutRect=rRect;
    }

    const tools::Rectangle& SdrObject::GetLogicRect() const
    {
        return GetSnapRect();
    }

    void SdrObject::NbcSetLogicRect(const tools::Rectangle& rRect)
    {
        NbcSetSnapRect(rRect);
    }

    void SdrObject::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool /* bShrinkOnly = false */ )
    {
        SetLogicRect( rMaxRect );
    }

    void SdrObject::SetSnapRect(const tools::Rectangle& rRect)
    {
        tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
        NbcSetSnapRect(rRect);
        SetChanged();
        BroadcastObjectChange();
        SendUserCall(SdrUserCallType::Resize,aBoundRect0);
    }

    void SdrObject::SetLogicRect(const tools::Rectangle& rRect)
    {
        tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
        NbcSetLogicRect(rRect);
        SetChanged();
        BroadcastObjectChange();
        SendUserCall(SdrUserCallType::Resize,aBoundRect0);
    }

    long SdrObject::GetRotateAngle() const
    {
        return 0;
    }

    long SdrObject::GetShearAngle(bool /*bVertical*/) const
    {
        return 0;
    }

    sal_uInt32 SdrObject::GetSnapPointCount() const
    {
        return GetPointCount();
    }

    Point SdrObject::GetSnapPoint(sal_uInt32 i) const
    {
        return GetPoint(i);
    }

    bool SdrObject::IsPolyObj() const
    {
        return false;
    }

    sal_uInt32 SdrObject::GetPointCount() const
    {
        return 0L;
    }

    Point SdrObject::GetPoint(sal_uInt32 /*i*/) const
    {
        return Point();
    }

    void SdrObject::SetPoint(const Point& rPnt, sal_uInt32 i)
    {
        tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
        NbcSetPoint(rPnt, i);
        SetChanged();
        BroadcastObjectChange();
        SendUserCall(SdrUserCallType::Resize,aBoundRect0);
    }

    void SdrObject::NbcSetPoint(const Point& /*rPnt*/, sal_uInt32 /*i*/)
    {
    }

    bool SdrObject::HasTextEdit() const
    {
        return false;
    }

    bool SdrObject::Equals(const SdrObject& rOtherObj) const
    {
        return (aAnchor.X() == rOtherObj.aAnchor.X() && aAnchor.Y() == rOtherObj.aAnchor.Y() &&
                aGridOffset.X() == rOtherObj.aGridOffset.X() && aGridOffset.Y() == rOtherObj.aGridOffset.Y() &&
                nOrdNum == rOtherObj.nOrdNum && mnNavigationPosition == rOtherObj.mnNavigationPosition &&
                mbSupportTextIndentingOnLineWidthChange == rOtherObj.mbSupportTextIndentingOnLineWidthChange &&
                mbLineIsOutsideGeometry == rOtherObj.mbLineIsOutsideGeometry && bMarkProt == rOtherObj.bMarkProt &&
                bIs3DObj == rOtherObj.bIs3DObj && bIsEdge == rOtherObj.bIsEdge && bClosedObj == rOtherObj.bClosedObj &&
                bNotVisibleAsMaster == rOtherObj.bNotVisibleAsMaster && bEmptyPresObj == rOtherObj.bEmptyPresObj &&
                mbVisible == rOtherObj.mbVisible && bNoPrint == rOtherObj.bNoPrint && bSizProt == rOtherObj.bSizProt &&
                bMovProt == rOtherObj.bMovProt && bVirtObj == rOtherObj.bVirtObj &&
                mnLayerID == rOtherObj.mnLayerID && GetMergedItemSet().Equals(rOtherObj.GetMergedItemSet(), false) );
    }

    void SdrObject::dumpAsXml(xmlTextWriterPtr pWriter) const
    {
        xmlTextWriterStartElement(pWriter, BAD_CAST("SdrObject"));
        xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
        xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name()));
        xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("name"), "%s", BAD_CAST(GetName().toUtf8().getStr()));
        xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("title"), "%s", BAD_CAST(GetTitle().toUtf8().getStr()));
        xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("description"), "%s", BAD_CAST(GetDescription().toUtf8().getStr()));
        xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("nOrdNum"), "%" SAL_PRIuUINT32, GetOrdNumDirect());
        xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aOutRect"), BAD_CAST(aOutRect.toString().getStr()));

        if (const OutlinerParaObject* pOutliner = GetOutlinerParaObject())
            pOutliner->dumpAsXml(pWriter);

        xmlTextWriterEndElement(pWriter);
    }

    void SdrObject::SetOutlinerParaObject(std::unique_ptr<OutlinerParaObject> pTextObject)
    {
        tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
        NbcSetOutlinerParaObject(std::move(pTextObject));
        SetChanged();
        BroadcastObjectChange();
        if (GetCurrentBoundRect()!=aBoundRect0) {
            SendUserCall(SdrUserCallType::Resize,aBoundRect0);
        }
    }

    void SdrObject::NbcSetOutlinerParaObject(std::unique_ptr<OutlinerParaObject> /*pTextObject*/)
    {
    }

    OutlinerParaObject* SdrObject::GetOutlinerParaObject() const
    {
        return nullptr;
    }

    void SdrObject::NbcReformatText()
    {
    }

    void SdrObject::ReformatText()
    {
        tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
        NbcReformatText();
        SetChanged();
        BroadcastObjectChange();
        if (GetCurrentBoundRect()!=aBoundRect0) {
            SendUserCall(SdrUserCallType::Resize,aBoundRect0);
        }
    }

    void SdrObject::BurnInStyleSheetAttributes()
    {
        GetProperties().ForceStyleToHardAttributes();
    }

    bool SdrObject::HasMacro() const
    {
        return false;
    }

    SdrObject* SdrObject::CheckMacroHit(const SdrObjMacroHitRec& rRec) const
    {
        if(rRec.pPageView)
        {
            return SdrObjectPrimitiveHit(*this, rRec.aPos, rRec.nTol, *rRec.pPageView, rRec.pVisiLayer, false);
        }

        return nullptr;
    }

    Pointer SdrObject::GetMacroPointer(const SdrObjMacroHitRec&) const
    {
        return Pointer(PointerStyle::RefHand);
    }

    void SdrObject::PaintMacro(OutputDevice& rOut, const tools::Rectangle& , const SdrObjMacroHitRec& ) const
    {
        const RasterOp eRop(rOut.GetRasterOp());
        const basegfx::B2DPolyPolygon aPolyPolygon(TakeXorPoly());
        const sal_uInt32 nCount(aPolyPolygon.count());

        rOut.SetLineColor(COL_BLACK);
        rOut.SetFillColor();
        rOut.SetRasterOp(RasterOp::Invert);

        for(sal_uInt32 a(0); a < nCount; a++)
        {
            rOut.DrawPolyLine(aPolyPolygon.getB2DPolygon(a));
        }

        rOut.SetRasterOp(eRop);
    }

    bool SdrObject::DoMacro(const SdrObjMacroHitRec&)
    {
        return false;
    }

    OUString SdrObject::GetMacroPopupComment(const SdrObjMacroHitRec&) const
    {
        return OUString();
    }

    bool SdrObject::IsMacroHit(const SdrObjMacroHitRec& rRec) const
    {
        return CheckMacroHit(rRec) != nullptr;
    }

    SdrObjGeoData* SdrObject::NewGeoData() const
    {
        return new SdrObjGeoData;
    }

    void SdrObject::SaveGeoData(SdrObjGeoData& rGeo) const
    {
        rGeo.aBoundRect    =GetCurrentBoundRect();
        rGeo.aAnchor       =aAnchor       ;
        rGeo.bMovProt      =bMovProt      ;
        rGeo.bSizProt      =bSizProt      ;
        rGeo.bNoPrint      =bNoPrint      ;
        rGeo.mbVisible     =mbVisible     ;
        rGeo.bClosedObj    =bClosedObj    ;
        rGeo.mnLayerID = mnLayerID;

        // user-defined glue points
        if (pPlusData!=nullptr && pPlusData->pGluePoints!=nullptr) {
            if (rGeo.pGPL!=nullptr) {
                *rGeo.pGPL=*pPlusData->pGluePoints;
            } else {
                rGeo.pGPL.reset( new SdrGluePointList(*pPlusData->pGluePoints) );
            }
        } else {
            rGeo.pGPL.reset();
        }
    }

    void SdrObject::RestGeoData(const SdrObjGeoData& rGeo)
    {
        SetRectsDirty();
        aOutRect      =rGeo.aBoundRect    ;
        aAnchor       =rGeo.aAnchor       ;
        bMovProt      =rGeo.bMovProt      ;
        bSizProt      =rGeo.bSizProt      ;
        bNoPrint      =rGeo.bNoPrint      ;
        mbVisible     =rGeo.mbVisible     ;
        bClosedObj    =rGeo.bClosedObj    ;
        mnLayerID = rGeo.mnLayerID;

        // user-defined glue points
        if (rGeo.pGPL!=nullptr) {
            ImpForcePlusData();
            if (pPlusData->pGluePoints!=nullptr) {
                *pPlusData->pGluePoints=*rGeo.pGPL;
            } else {
                pPlusData->pGluePoints.reset(new SdrGluePointList(*rGeo.pGPL));
            }
        } else {
            if (pPlusData!=nullptr && pPlusData->pGluePoints!=nullptr) {
                pPlusData->pGluePoints.reset();
            }
        }
    }

    SdrObjGeoData* SdrObject::GetGeoData() const
    {
        SdrObjGeoData* pGeo=NewGeoData();
        SaveGeoData(*pGeo);
        return pGeo;
    }

    void SdrObject::SetGeoData(const SdrObjGeoData& rGeo)
    {
        tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
        RestGeoData(rGeo);
        SetChanged();
        BroadcastObjectChange();
        SendUserCall(SdrUserCallType::Resize,aBoundRect0);
    }

    // ItemSet access

    const SfxItemSet& SdrObject::GetObjectItemSet() const
    {
        return GetProperties().GetObjectItemSet();
    }

    const SfxItemSet& SdrObject::GetMergedItemSet() const
    {
        return GetProperties().GetMergedItemSet();
    }

    void SdrObject::SetObjectItem(const SfxPoolItem& rItem)
    {
        GetProperties().SetObjectItem(rItem);
    }

    void SdrObject::SetMergedItem(const SfxPoolItem& rItem)
    {
        GetProperties().SetMergedItem(rItem);
    }

    void SdrObject::ClearMergedItem(const sal_uInt16 nWhich)
    {
        GetProperties().ClearMergedItem(nWhich);
    }

    void SdrObject::SetObjectItemSet(const SfxItemSet& rSet)
    {
        GetProperties().SetObjectItemSet(rSet);
    }

    void SdrObject::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems)
    {
        GetProperties().SetMergedItemSet(rSet, bClearAllItems);
    }

    const SfxPoolItem& SdrObject::GetObjectItem(const sal_uInt16 nWhich) const
    {
        return GetObjectItemSet().Get(nWhich);
    }

    const SfxPoolItem& SdrObject::GetMergedItem(const sal_uInt16 nWhich) const
    {
        return GetMergedItemSet().Get(nWhich);
    }

    void SdrObject::SetMergedItemSetAndBroadcast(const SfxItemSet& rSet, bool bClearAllItems)
    {
        GetProperties().SetMergedItemSetAndBroadcast(rSet, bClearAllItems);
    }

    void SdrObject::ApplyNotPersistAttr(const SfxItemSet& rAttr)
    {
        tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
        NbcApplyNotPersistAttr(rAttr);
        SetChanged();
        BroadcastObjectChange();
        SendUserCall(SdrUserCallType::Resize,aBoundRect0);
    }

    void SdrObject::NbcApplyNotPersistAttr(const SfxItemSet& rAttr)
    {
        const tools::Rectangle& rSnap=GetSnapRect();
        const tools::Rectangle& rLogic=GetLogicRect();
        Point aRef1(rSnap.Center());
        const SfxPoolItem *pPoolItem=nullptr;
        if (rAttr.GetItemState(SDRATTR_TRANSFORMREF1X,true,&pPoolItem)==SfxItemState::SET) {
            aRef1.setX(static_cast<const SdrTransformRef1XItem*>(pPoolItem)->GetValue() );
        }
        if (rAttr.GetItemState(SDRATTR_TRANSFORMREF1Y,true,&pPoolItem)==SfxItemState::SET) {
            aRef1.setY(static_cast<const SdrTransformRef1YItem*>(pPoolItem)->GetValue() );
        }

        tools::Rectangle aNewSnap(rSnap);
        if (rAttr.GetItemState(SDRATTR_MOVEX,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrMoveXItem*>(pPoolItem)->GetValue();
            aNewSnap.Move(n,0);
        }
        if (rAttr.GetItemState(SDRATTR_MOVEY,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrMoveYItem*>(pPoolItem)->GetValue();
            aNewSnap.Move(0,n);
        }
        if (rAttr.GetItemState(SDRATTR_ONEPOSITIONX,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrOnePositionXItem*>(pPoolItem)->GetValue();
            aNewSnap.Move(n-aNewSnap.Left(),0);
        }
        if (rAttr.GetItemState(SDRATTR_ONEPOSITIONY,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrOnePositionYItem*>(pPoolItem)->GetValue();
            aNewSnap.Move(0,n-aNewSnap.Top());
        }
        if (rAttr.GetItemState(SDRATTR_ONESIZEWIDTH,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrOneSizeWidthItem*>(pPoolItem)->GetValue();
            aNewSnap.SetRight(aNewSnap.Left()+n );
        }
        if (rAttr.GetItemState(SDRATTR_ONESIZEHEIGHT,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrOneSizeHeightItem*>(pPoolItem)->GetValue();
            aNewSnap.SetBottom(aNewSnap.Top()+n );
        }
        if (aNewSnap!=rSnap) {
            if (aNewSnap.GetSize()==rSnap.GetSize()) {
                NbcMove(Size(aNewSnap.Left()-rSnap.Left(),aNewSnap.Top()-rSnap.Top()));
            } else {
                NbcSetSnapRect(aNewSnap);
            }
        }

        if (rAttr.GetItemState(SDRATTR_SHEARANGLE,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrShearAngleItem*>(pPoolItem)->GetValue();
            n-=GetShearAngle();
            if (n!=0) {
                double nTan=tan(n*nPi180);
                NbcShear(aRef1,n,nTan,false);
            }
        }
        if (rAttr.GetItemState(SDRATTR_ROTATEANGLE,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrAngleItem*>(pPoolItem)->GetValue();
            n-=GetRotateAngle();
            if (n!=0) {
                double nSin=sin(n*nPi180);
                double nCos=cos(n*nPi180);
                NbcRotate(aRef1,n,nSin,nCos);
            }
        }
        if (rAttr.GetItemState(SDRATTR_ROTATEONE,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrRotateOneItem*>(pPoolItem)->GetValue();
            double nSin=sin(n*nPi180);
            double nCos=cos(n*nPi180);
            NbcRotate(aRef1,n,nSin,nCos);
        }
        if (rAttr.GetItemState(SDRATTR_HORZSHEARONE,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrHorzShearOneItem*>(pPoolItem)->GetValue();
            double nTan=tan(n*nPi180);
            NbcShear(aRef1,n,nTan,false);
        }
        if (rAttr.GetItemState(SDRATTR_VERTSHEARONE,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrVertShearOneItem*>(pPoolItem)->GetValue();
            double nTan=tan(n*nPi180);
            NbcShear(aRef1,n,nTan,true);
        }

        if (rAttr.GetItemState(SDRATTR_OBJMOVEPROTECT,true,&pPoolItem)==SfxItemState::SET) {
            bool b=static_cast<const SdrYesNoItem*>(pPoolItem)->GetValue();
            SetMoveProtect(b);
        }
        if (rAttr.GetItemState(SDRATTR_OBJSIZEPROTECT,true,&pPoolItem)==SfxItemState::SET) {
            bool b=static_cast<const SdrYesNoItem*>(pPoolItem)->GetValue();
            SetResizeProtect(b);
        }

        /* move protect always sets size protect */
        if( IsMoveProtect() )
            SetResizeProtect( true );

        if (rAttr.GetItemState(SDRATTR_OBJPRINTABLE,true,&pPoolItem)==SfxItemState::SET) {
            bool b=static_cast<const SdrObjPrintableItem*>(pPoolItem)->GetValue();
            SetPrintable(b);
        }

        if (rAttr.GetItemState(SDRATTR_OBJVISIBLE,true,&pPoolItem)==SfxItemState::SET) {
            bool b=static_cast<const SdrObjVisibleItem*>(pPoolItem)->GetValue();
            SetVisible(b);
        }

        SdrLayerID nLayer=SDRLAYER_NOTFOUND;
        if (rAttr.GetItemState(SDRATTR_LAYERID,true,&pPoolItem)==SfxItemState::SET) {
            nLayer=static_cast<const SdrLayerIdItem*>(pPoolItem)->GetValue();
        }
        if (rAttr.GetItemState(SDRATTR_LAYERNAME,true,&pPoolItem)==SfxItemState::SET)
        {
            OUString aLayerName = static_cast<const SdrLayerNameItem*>(pPoolItem)->GetValue();
            const SdrLayerAdmin& rLayAd(nullptr != getSdrPageFromSdrObject()
                ? getSdrPageFromSdrObject()->GetLayerAdmin()
                : getSdrModelFromSdrObject().GetLayerAdmin());
            const SdrLayer* pLayer = rLayAd.GetLayer(aLayerName);

            if(nullptr != pLayer)
            {
                nLayer=pLayer->GetID();
            }
        }
        if (nLayer!=SDRLAYER_NOTFOUND) {
            NbcSetLayer(nLayer);
        }

        if (rAttr.GetItemState(SDRATTR_OBJECTNAME,true,&pPoolItem)==SfxItemState::SET) {
            OUString aName=static_cast<const SfxStringItem*>(pPoolItem)->GetValue();
            SetName(aName);
        }
        tools::Rectangle aNewLogic(rLogic);
        if (rAttr.GetItemState(SDRATTR_LOGICSIZEWIDTH,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrLogicSizeWidthItem*>(pPoolItem)->GetValue();
            aNewLogic.SetRight(aNewLogic.Left()+n );
        }
        if (rAttr.GetItemState(SDRATTR_LOGICSIZEHEIGHT,true,&pPoolItem)==SfxItemState::SET) {
            long n=static_cast<const SdrLogicSizeHeightItem*>(pPoolItem)->GetValue();
            aNewLogic.SetBottom(aNewLogic.Top()+n );
        }
        if (aNewLogic!=rLogic) {
            NbcSetLogicRect(aNewLogic);
        }
        Fraction aResizeX(1,1);
        Fraction aResizeY(1,1);
        if (rAttr.GetItemState(SDRATTR_RESIZEXONE,true,&pPoolItem)==SfxItemState::SET) {
            aResizeX*=static_cast<const SdrResizeXOneItem*>(pPoolItem)->GetValue();
        }
        if (rAttr.GetItemState(SDRATTR_RESIZEYONE,true,&pPoolItem)==SfxItemState::SET) {
            aResizeY*=static_cast<const SdrResizeYOneItem*>(pPoolItem)->GetValue();
        }
        if (aResizeX!=Fraction(1,1) || aResizeY!=Fraction(1,1)) {
            NbcResize(aRef1,aResizeX,aResizeY);
        }
    }

    void SdrObject::TakeNotPersistAttr(SfxItemSet& rAttr) const
    {
        const tools::Rectangle& rSnap=GetSnapRect();
        const tools::Rectangle& rLogic=GetLogicRect();
        rAttr.Put(SdrYesNoItem(SDRATTR_OBJMOVEPROTECT, IsMoveProtect()));
        rAttr.Put(SdrYesNoItem(SDRATTR_OBJSIZEPROTECT, IsResizeProtect()));
        rAttr.Put(SdrObjPrintableItem(IsPrintable()));
        rAttr.Put(SdrObjVisibleItem(IsVisible()));
        rAttr.Put(SdrAngleItem(SDRATTR_ROTATEANGLE, GetRotateAngle()));
        rAttr.Put(SdrShearAngleItem(GetShearAngle()));
        rAttr.Put(SdrOneSizeWidthItem(rSnap.GetWidth()-1));
        rAttr.Put(SdrOneSizeHeightItem(rSnap.GetHeight()-1));
        rAttr.Put(SdrOnePositionXItem(rSnap.Left()));
        rAttr.Put(SdrOnePositionYItem(rSnap.Top()));
        if (rLogic.GetWidth()!=rSnap.GetWidth()) {
            rAttr.Put(SdrLogicSizeWidthItem(rLogic.GetWidth()-1));
        }
        if (rLogic.GetHeight()!=rSnap.GetHeight()) {
            rAttr.Put(SdrLogicSizeHeightItem(rLogic.GetHeight()-1));
        }
        OUString aName(GetName());

        if (!aName.isEmpty())
        {
            rAttr.Put(SfxStringItem(SDRATTR_OBJECTNAME, aName));
        }

        rAttr.Put(SdrLayerIdItem(GetLayer()));
        const SdrLayerAdmin& rLayAd(nullptr != getSdrPageFromSdrObject()
            ? getSdrPageFromSdrObject()->GetLayerAdmin()
            : getSdrModelFromSdrObject().GetLayerAdmin());
        const SdrLayer* pLayer = rLayAd.GetLayerPerID(GetLayer());
        if(nullptr != pLayer)
        {
            rAttr.Put(SdrLayerNameItem(pLayer->GetName()));
        }
        Point aRef1(rSnap.Center());
        Point aRef2(aRef1); aRef2.AdjustY( 1 );
        rAttr.Put(SdrTransformRef1XItem(aRef1.X()));
        rAttr.Put(SdrTransformRef1YItem(aRef1.Y()));
        rAttr.Put(SdrTransformRef2XItem(aRef2.X()));
        rAttr.Put(SdrTransformRef2YItem(aRef2.Y()));
    }

    SfxStyleSheet* SdrObject::GetStyleSheet() const
    {
        return GetProperties().GetStyleSheet();
    }

    void SdrObject::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr)
    {
        tools::Rectangle aBoundRect0;

        if(pUserCall)
            aBoundRect0 = GetLastBoundRect();

        NbcSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
        SetChanged();
        BroadcastObjectChange();
        SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect0);
    }

    void SdrObject::NbcSetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr)
    {
        // only allow graphic and presentation styles for shapes
        if( pNewStyleSheet && (pNewStyleSheet->GetFamily() == SfxStyleFamily::Para) && (pNewStyleSheet->GetFamily() == SfxStyleFamily::Page) )
            return;

        GetProperties().SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
    }

    // Broadcasting while setting attributes is managed by the AttrObj.

    bool SdrObject::IsNode() const
    {
        return true;
    }

    SdrGluePoint SdrObject::GetVertexGluePoint(sal_uInt16 nPosNum) const
    {
        // #i41936# Use SnapRect for default GluePoints
        const tools::Rectangle aR(GetSnapRect());
        Point aPt;

        switch(nPosNum)
        {
            case 0 : aPt = aR.TopCenter();    break;
            case 1 : aPt = aR.RightCenter();  break;
            case 2 : aPt = aR.BottomCenter(); break;
            case 3 : aPt = aR.LeftCenter();   break;
        }

        aPt -= aR.Center();
        SdrGluePoint aGP(aPt);
        aGP.SetPercent(false);

        return aGP;
    }

    SdrGluePoint SdrObject::GetCornerGluePoint(sal_uInt16 nPosNum) const
    {
        tools::Rectangle aR(GetCurrentBoundRect());
        Point aPt;
        switch (nPosNum) {
            case 0 : aPt=aR.TopLeft();     break;
            case 1 : aPt=aR.TopRight();    break;
            case 2 : aPt=aR.BottomRight(); break;
            case 3 : aPt=aR.BottomLeft();  break;
        }
        aPt-=GetSnapRect().Center();
        SdrGluePoint aGP(aPt);
        aGP.SetPercent(false);
        return aGP;
    }

    const SdrGluePointList* SdrObject::GetGluePointList() const
    {
        if (pPlusData!=nullptr) return pPlusData->pGluePoints.get();
        return nullptr;
    }

    SdrGluePointList* SdrObject::ForceGluePointList()
    {
        ImpForcePlusData();
        if (pPlusData->pGluePoints==nullptr) {
            pPlusData->pGluePoints.reset(new SdrGluePointList);
        }
        return pPlusData->pGluePoints.get();
    }

    void SdrObject::SetGlueReallyAbsolute(bool bOn)
    {
        // First a const call to see whether there are any glue points.
        // Force const call!
        if (GetGluePointList()!=nullptr) {
            SdrGluePointList* pGPL=ForceGluePointList();
            pGPL->SetReallyAbsolute(bOn,*this);
        }
    }

    void SdrObject::NbcRotateGluePoints(const Point& rRef, long nAngle, double sn, double cs)
    {
        // First a const call to see whether there are any glue points.
        // Force const call!
        if (GetGluePointList()!=nullptr) {
            SdrGluePointList* pGPL=ForceGluePointList();
            pGPL->Rotate(rRef,nAngle,sn,cs,this);
        }
    }

    void SdrObject::NbcMirrorGluePoints(const Point& rRef1, const Point& rRef2)
    {
        // First a const call to see whether there are any glue points.
        // Force const call!
        if (GetGluePointList()!=nullptr) {
            SdrGluePointList* pGPL=ForceGluePointList();
            pGPL->Mirror(rRef1,rRef2,this);
        }
    }

    void SdrObject::NbcShearGluePoints(const Point& rRef, double tn, bool bVShear)
    {
        // First a const call to see whether there are any glue points.
        // Force const call!
        if (GetGluePointList()!=nullptr) {
            SdrGluePointList* pGPL=ForceGluePointList();
            pGPL->Shear(rRef,tn,bVShear,this);
        }
    }

    void SdrObject::ConnectToNode(bool /*bTail1*/, SdrObject* /*pObj*/)
    {
    }

    void SdrObject::DisconnectFromNode(bool /*bTail1*/)
    {
    }

    SdrObject* SdrObject::GetConnectedNode(bool /*bTail1*/) const
    {
        return nullptr;
    }

    void extractLineContourFromPrimitive2DSequence(
        const drawinglayer::primitive2d::Primitive2DContainer& rxSequence,
        basegfx::B2DPolygonVector& rExtractedHairlines,
        basegfx::B2DPolyPolygonVector& rExtractedLineFills)
    {
        rExtractedHairlines.clear();
        rExtractedLineFills.clear();

        if(!rxSequence.empty())
        {
            // use neutral ViewInformation
            const drawinglayer::geometry::ViewInformation2D aViewInformation2D;

            // create extractor, process and get result
            drawinglayer::processor2d::LineGeometryExtractor2D aExtractor(aViewInformation2D);
            aExtractor.process(rxSequence);

            // copy line results
            rExtractedHairlines = aExtractor.getExtractedHairlines();

            // copy fill rsults
            rExtractedLineFills = aExtractor.getExtractedLineFills();
        }
    }

    SdrObject* SdrObject::ImpConvertToContourObj(bool bForceLineDash)
    {
        SdrObject* pRetval(nullptr);

        if(LineGeometryUsageIsNecessary())
        {
            basegfx::B2DPolyPolygon aMergedLineFillPolyPolygon;
            basegfx::B2DPolyPolygon aMergedHairlinePolyPolygon;
            const drawinglayer::primitive2d::Primitive2DContainer xSequence(GetViewContact().getViewIndependentPrimitive2DContainer());

            if(!xSequence.empty())
            {
                basegfx::B2DPolygonVector aExtractedHairlines;
                basegfx::B2DPolyPolygonVector aExtractedLineFills;

                extractLineContourFromPrimitive2DSequence(xSequence, aExtractedHairlines, aExtractedLineFills);

                if(!aExtractedHairlines.empty())
                {
                    // for SdrObject creation, just copy all to a single Hairline-PolyPolygon
                    for(basegfx::B2DPolygon & rExtractedHairline : aExtractedHairlines)
                    {
                        aMergedHairlinePolyPolygon.append(rExtractedHairline);
                    }
                }

                // check for fill rsults
                if (!aExtractedLineFills.empty() && !utl::ConfigManager::IsFuzzing())
                {
                    // merge to a single tools::PolyPolygon (OR)
                    aMergedLineFillPolyPolygon = basegfx::utils::mergeToSinglePolyPolygon(aExtractedLineFills);
                }
            }

            if(aMergedLineFillPolyPolygon.count() || (bForceLineDash && aMergedHairlinePolyPolygon.count()))
            {
                SfxItemSet aSet(GetMergedItemSet());
                drawing::FillStyle eOldFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue();
                SdrPathObj* aLinePolygonPart = nullptr;
                SdrPathObj* aLineHairlinePart = nullptr;
                bool bBuildGroup(false);

                if(aMergedLineFillPolyPolygon.count())
                {
                    // create SdrObject for filled line geometry
                    aLinePolygonPart = new SdrPathObj(
                        getSdrModelFromSdrObject(),
                        OBJ_PATHFILL,
                        aMergedLineFillPolyPolygon);

                    // correct item properties
                    aSet.Put(XLineWidthItem(0));
                    aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
                    Color aColorLine = aSet.Get(XATTR_LINECOLOR).GetColorValue();
                    sal_uInt16 nTransLine = aSet.Get(XATTR_LINETRANSPARENCE).GetValue();
                    aSet.Put(XFillColorItem(OUString(), aColorLine));
                    aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
                    aSet.Put(XFillTransparenceItem(nTransLine));

                    aLinePolygonPart->SetMergedItemSet(aSet);
                }

                if(aMergedHairlinePolyPolygon.count())
                {
                    // create SdrObject for hairline geometry
                    // OBJ_PATHLINE is necessary here, not OBJ_PATHFILL. This is intended
                    // to get a non-filled object. If the poly is closed, the PathObj takes care for
                    // the correct closed state.
                    aLineHairlinePart = new SdrPathObj(
                        getSdrModelFromSdrObject(),
                        OBJ_PATHLINE,
                        aMergedHairlinePolyPolygon);

                    aSet.Put(XLineWidthItem(0));
                    aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
                    aSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));

                    // it is also necessary to switch off line start and ends here
                    aSet.Put(XLineStartWidthItem(0));
                    aSet.Put(XLineEndWidthItem(0));

                    aLineHairlinePart->SetMergedItemSet(aSet);

                    if(aLinePolygonPart)
                    {
                        bBuildGroup = true;
                    }
                }

                // check if original geometry should be added (e.g. filled and closed)
                bool bAddOriginalGeometry(false);
                SdrPathObj* pPath = dynamic_cast<SdrPathObj*>(this);

                if(pPath && pPath->IsClosed())
                {
                    if(eOldFillStyle != drawing::FillStyle_NONE)
                    {
                        bAddOriginalGeometry = true;
                    }
                }

                // do we need a group?
                if(bBuildGroup || bAddOriginalGeometry)
                {
                    SdrObject* pGroup = new SdrObjGroup(getSdrModelFromSdrObject());

                    if(bAddOriginalGeometry)
                    {
                        // Add a clone of the original geometry.
                        aSet.ClearItem();
                        aSet.Put(GetMergedItemSet());
                        aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
                        aSet.Put(XLineWidthItem(0));

                        SdrObject* pClone(CloneSdrObject(getSdrModelFromSdrObject()));
                        pClone->SetMergedItemSet(aSet);

                        pGroup->GetSubList()->NbcInsertObject(pClone);
                    }

                    if(aLinePolygonPart)
                    {
                        pGroup->GetSubList()->NbcInsertObject(aLinePolygonPart);
                    }

                    if(aLineHairlinePart)
                    {
                        pGroup->GetSubList()->NbcInsertObject(aLineHairlinePart);
                    }

                    pRetval = pGroup;
                }
                else
                {
                    if(aLinePolygonPart)
                    {
                        pRetval = aLinePolygonPart;
                    }
                    else if(aLineHairlinePart)
                    {
                        pRetval = aLineHairlinePart;
                    }
                }
            }
        }

        if(nullptr == pRetval)
        {
            // due to current method usage, create and return a clone when nothing has changed
            SdrObject* pClone(CloneSdrObject(getSdrModelFromSdrObject()));
            pRetval = pClone;
        }

        return pRetval;
    }

    void SdrObject::SetMarkProtect(bool bProt)
    {
        bMarkProt = bProt;
    }

    void SdrObject::SetEmptyPresObj(bool bEpt)
    {
        bEmptyPresObj = bEpt;
    }

    void SdrObject::SetNotVisibleAsMaster(bool bFlg)
    {
        bNotVisibleAsMaster=bFlg;
    }

    // convert this path object to contour object, even when it is a group
    SdrObject* SdrObject::ConvertToContourObj(SdrObject* pRet, bool bForceLineDash) const
    {
        if(dynamic_cast<const SdrObjGroup*>( pRet) !=  nullptr)
        {
            SdrObjList* pObjList2 = pRet->GetSubList();
            SdrObject* pGroup = new SdrObjGroup(getSdrModelFromSdrObject());

            for(size_t a=0; a<pObjList2->GetObjCount(); ++a)
            {
                SdrObject* pIterObj = pObjList2->GetObj(a);
                pGroup->GetSubList()->NbcInsertObject(ConvertToContourObj(pIterObj, bForceLineDash));
            }

            pRet = pGroup;
        }
        else
        {
            if (SdrPathObj *pPathObj = dynamic_cast<SdrPathObj*>(pRet))
            {
                // bezier geometry got created, even for straight edges since the given
                // object is a result of DoConvertToPolyObj. For conversion to contour
                // this is not really needed and can be reduced again AFAP
                pPathObj->SetPathPoly(basegfx::utils::simplifyCurveSegments(pPathObj->GetPathPoly()));
            }

            pRet = pRet->ImpConvertToContourObj(bForceLineDash);
        }

        // #i73441# preserve LayerID
        if(pRet && pRet->GetLayer() != GetLayer())
        {
            pRet->SetLayer(GetLayer());
        }

        return pRet;
    }

    SdrObject* SdrObject::ConvertToPolyObj(bool bBezier, bool bLineToArea) const
    {
        SdrObject* pRet = DoConvertToPolyObj(bBezier, true);

        if(pRet && bLineToArea)
        {
            SdrObject* pNewRet = ConvertToContourObj(pRet);
            delete pRet;
            pRet = pNewRet;
        }

        // #i73441# preserve LayerID
        if(pRet && pRet->GetLayer() != GetLayer())
        {
            pRet->SetLayer(GetLayer());
        }

        return pRet;
    }

    SdrObject* SdrObject::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const
    {
        return nullptr;
    }

    void SdrObject::InsertedStateChange()
    {
        const bool bIsInserted(nullptr != getParentSdrObjListFromSdrObject());
        const tools::Rectangle aBoundRect0(GetLastBoundRect());

        if(bIsInserted)
        {
            SendUserCall(SdrUserCallType::Inserted, aBoundRect0);
        }
        else
        {
            SendUserCall(SdrUserCallType::Removed, aBoundRect0);
        }

        if(nullptr != pPlusData && nullptr != pPlusData->pBroadcast)
        {
            SdrHint aHint(bIsInserted ? SdrHintKind::ObjectInserted : SdrHintKind::ObjectRemoved, *this);
            pPlusData->pBroadcast->Broadcast(aHint);
        }
    }

    void SdrObject::SetMoveProtect(bool bProt)
    {
        if(IsMoveProtect() != bProt)
        {
            // #i77187# secured and simplified
            bMovProt = bProt;
            SetChanged();
            BroadcastObjectChange();
        }
    }

    void SdrObject::SetResizeProtect(bool bProt)
    {
        if(IsResizeProtect() != bProt)
        {
            // #i77187# secured and simplified
            bSizProt = bProt;
            SetChanged();
            BroadcastObjectChange();
        }
    }

    void SdrObject::SetPrintable(bool bPrn)
    {
        if( bPrn == bNoPrint )
        {
            bNoPrint=!bPrn;
            SetChanged();
            if (IsInserted())
            {
                SdrHint aHint(SdrHintKind::ObjectChange, *this);
                getSdrModelFromSdrObject().Broadcast(aHint);
            }
        }
    }

    void SdrObject::SetVisible(bool bVisible)
    {
        if( bVisible != mbVisible )
        {
            mbVisible = bVisible;
            SetChanged();
            if (IsInserted())
            {
                SdrHint aHint(SdrHintKind::ObjectChange, *this);
                getSdrModelFromSdrObject().Broadcast(aHint);
            }
        }
    }

    sal_uInt16 SdrObject::GetUserDataCount() const
    {
        if (pPlusData==nullptr || pPlusData->pUserDataList==nullptr) return 0;
        return pPlusData->pUserDataList->GetUserDataCount();
    }

    SdrObjUserData* SdrObject::GetUserData(sal_uInt16 nNum) const
    {
        if (pPlusData==nullptr || pPlusData->pUserDataList==nullptr) return nullptr;
        return &pPlusData->pUserDataList->GetUserData(nNum);
    }

    void SdrObject::AppendUserData(std::unique_ptr<SdrObjUserData> pData)
    {
        if (!pData)
        {
            OSL_FAIL("SdrObject::AppendUserData(): pData is NULL pointer.");
            return;
        }

        ImpForcePlusData();
        if (!pPlusData->pUserDataList)
            pPlusData->pUserDataList.reset( new SdrObjUserDataList );

        pPlusData->pUserDataList->AppendUserData(std::move(pData));
    }

    void SdrObject::DeleteUserData(sal_uInt16 nNum)
    {
        sal_uInt16 nCount=GetUserDataCount();
        if (nNum<nCount) {
            pPlusData->pUserDataList->DeleteUserData(nNum);
            if (nCount==1)  {
                pPlusData->pUserDataList.reset();
            }
        } else {
            OSL_FAIL("SdrObject::DeleteUserData(): Invalid Index.");
        }
    }

    void SdrObject::SetUserCall(SdrObjUserCall* pUser)
    {
        pUserCall = pUser;
    }

    void SdrObject::SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle& rBoundRect) const
    {
        SdrObject* pGroup(getParentSdrObjectFromSdrObject());

        if ( pUserCall )
        {
            pUserCall->Changed( *this, eUserCall, rBoundRect );
        }

        if(nullptr != pGroup && pGroup->GetUserCall())
        {
            // broadcast to group
            SdrUserCallType eChildUserType = SdrUserCallType::ChildChangeAttr;

            switch( eUserCall )
            {
                case SdrUserCallType::MoveOnly:
                    eChildUserType = SdrUserCallType::ChildMoveOnly;
                break;

                case SdrUserCallType::Resize:
                    eChildUserType = SdrUserCallType::ChildResize;
                break;

                case SdrUserCallType::ChangeAttr:
                    eChildUserType = SdrUserCallType::ChildChangeAttr;
                break;

                case SdrUserCallType::Delete:
                    eChildUserType = SdrUserCallType::ChildDelete;
                break;

                case SdrUserCallType::Inserted:
                    eChildUserType = SdrUserCallType::ChildInserted;
                break;

                case SdrUserCallType::Removed:
                    eChildUserType = SdrUserCallType::ChildRemoved;
                break;

                default: break;
            }

            pGroup->GetUserCall()->Changed( *this, eChildUserType, rBoundRect );
        }

        // notify our UNO shape listeners
        switch ( eUserCall )
        {
        case SdrUserCallType::Resize:
            notifyShapePropertyChange( svx::ShapeProperty::Size );
            SAL_FALLTHROUGH; // RESIZE might also imply a change of the position
        case SdrUserCallType::MoveOnly:
            notifyShapePropertyChange( svx::ShapeProperty::Position );
            break;
        default:
            // not interested in
            break;
        }
    }

    void SdrObject::impl_setUnoShape( const uno::Reference< uno::XInterface >& _rxUnoShape )
    {
        const uno::Reference< uno::XInterface>& xOldUnoShape( maWeakUnoShape );
        // the UNO shape would be gutted by the following code; return early
        if ( _rxUnoShape == xOldUnoShape )
        {
            if ( !xOldUnoShape.is() )
            {
                // make sure there is no stale impl. pointer if the UNO
                // shape was destroyed meanwhile (remember we only hold weak
                // reference to it!)
                mpSvxShape = nullptr;
            }
            return;
        }

        bool bTransferOwnership( false );
        if ( xOldUnoShape.is() )
        {
            bTransferOwnership = mpSvxShape->HasSdrObjectOwnership();
            // Remove yourself from the current UNO shape. Its destructor
            // will reset our UNO shape otherwise.
            mpSvxShape->InvalidateSdrObject();
        }

        maWeakUnoShape = _rxUnoShape;
        mpSvxShape = SvxShape::getImplementation( _rxUnoShape );

        // I think this may never happen... But I am not sure enough .-)
        if ( bTransferOwnership )
        {
            if (mpSvxShape)
                mpSvxShape->TakeSdrObjectOwnership();
            SAL_WARN( "svx.uno", "a UNO shape took over an SdrObject previously owned by another UNO shape!");
        }
    }

    /** only for internal use! */
    SvxShape* SdrObject::getSvxShape()
    {
        DBG_TESTSOLARMUTEX();
            // retrieving the impl pointer and subsequently using it is not thread-safe, of course, so it needs to be
            // guarded by the SolarMutex

        uno::Reference< uno::XInterface > xShape( maWeakUnoShape );
    #if OSL_DEBUG_LEVEL > 0
        OSL_ENSURE( !( !xShape.is() && mpSvxShape ),
            "SdrObject::getSvxShape: still having IMPL-Pointer to dead object!" );
    #endif
        //#113608#, make sure mpSvxShape is always synchronized with maWeakUnoShape
        if ( mpSvxShape && !xShape.is() )
            mpSvxShape = nullptr;

        return mpSvxShape;
    }

    css::uno::Reference< css::uno::XInterface > SdrObject::getUnoShape()
    {
        // try weak reference first
        uno::Reference< uno::XInterface > xShape( getWeakUnoShape() );
        if( !xShape.is() )
        {
            OSL_ENSURE( mpSvxShape == nullptr, "SdrObject::getUnoShape: XShape already dead, but still an IMPL pointer!" );
            if ( getSdrPageFromSdrObject() )
            {
                uno::Reference< uno::XInterface > xPage( getSdrPageFromSdrObject()->getUnoPage() );
                if( xPage.is() )
                {
                    SvxDrawPage* pDrawPage = SvxDrawPage::getImplementation(xPage);
                    if( pDrawPage )
                    {
                        // create one
                        xShape = pDrawPage->CreateShape( this );
                        impl_setUnoShape( xShape );
                    }
                }
            }
            else
            {
                mpSvxShape = SvxDrawPage::CreateShapeByTypeAndInventor( GetObjIdentifier(), GetObjInventor(), this );
                maWeakUnoShape = xShape = static_cast< ::cppu::OWeakObject* >( mpSvxShape );
            }
        }

        return xShape;
    }

    void SdrObject::setUnoShape(const uno::Reference<uno::XInterface >& _rxUnoShape)
    {
        impl_setUnoShape( _rxUnoShape );
    }

    svx::PropertyChangeNotifier& SdrObject::getShapePropertyChangeNotifier()
    {
        DBG_TESTSOLARMUTEX();

        SvxShape* pSvxShape = getSvxShape();
        ENSURE_OR_THROW( pSvxShape, "no SvxShape, yet!" );
        return pSvxShape->getShapePropertyChangeNotifier();
    }

    void SdrObject::notifyShapePropertyChange( const svx::ShapeProperty _eProperty ) const
    {
        DBG_TESTSOLARMUTEX();

        SvxShape* pSvxShape = const_cast< SdrObject* >( this )->getSvxShape();
        if ( pSvxShape )
            return pSvxShape->getShapePropertyChangeNotifier().notifyPropertyChange( _eProperty );
    }

    // transformation interface for StarOfficeAPI. This implements support for
    // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
    // moment it contains a shearX, rotation and translation, but for setting all linear
    // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.

    // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
    // with the base geometry and returns TRUE. Otherwise it returns FALSE.
    bool SdrObject::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
    {
        // any kind of SdrObject, just use SnapRect
        tools::Rectangle aRectangle(GetSnapRect());

        // convert to transformation values
        basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
        basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());

        // position maybe relative to anchorpos, convert
        if(getSdrModelFromSdrObject().IsWriter())
        {
            if(GetAnchorPos().X() || GetAnchorPos().Y())
            {
                aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
            }
        }

        // build matrix
        rMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(aScale, aTranslate);

        return false;
    }

    // sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
    // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
    // to use (0,0) as upper left and will be scaled to the given size in the matrix.
    void SdrObject::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
    {
        // break up matrix
        basegfx::B2DTuple aScale;
        basegfx::B2DTuple aTranslate;
        double fRotate, fShearX;
        rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);

        // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
        // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
        if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
        {
            aScale.setX(fabs(aScale.getX()));
            aScale.setY(fabs(aScale.getY()));
        }

        // if anchor is used, make position relative to it
        if(getSdrModelFromSdrObject().IsWriter())
        {
            if(GetAnchorPos().X() || GetAnchorPos().Y())
            {
                aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
            }
        }

        // build BaseRect
        Point aPoint(FRound(aTranslate.getX()), FRound(aTranslate.getY()));
        tools::Rectangle aBaseRect(aPoint, Size(FRound(aScale.getX()), FRound(aScale.getY())));

        // set BaseRect
        SetSnapRect(aBaseRect);
    }

    // Give info if object is in destruction
    bool SdrObject::IsInDestruction() const
    {
        return getSdrModelFromSdrObject().IsInDestruction();
    }

    // return if fill is != drawing::FillStyle_NONE
    bool SdrObject::HasFillStyle() const
    {
        return GetObjectItem(XATTR_FILLSTYLE).GetValue() != drawing::FillStyle_NONE;
    }

    bool SdrObject::HasLineStyle() const
    {
        return GetObjectItem(XATTR_LINESTYLE).GetValue() != drawing::LineStyle_NONE;
    }

    // #i52224#
    // on import of OLE object from MS documents the BLIP size might be retrieved,
    // the following four methods are used to control it;
    // usually this data makes no sense after the import is finished, since the object
    // might be resized

    void SdrObject::SetBLIPSizeRectangle( const tools::Rectangle& aRect )
    {
        maBLIPSizeRectangle = aRect;
    }

    void SdrObject::SetContextWritingMode( const sal_Int16 /*_nContextWritingMode*/ )
    {
        // this base class does not support different writing modes, so ignore the call
    }

    void SdrObject::SetDoNotInsertIntoPageAutomatically(const bool bSet)
    {
        mbDoNotInsertIntoPageAutomatically = bSet;
    }

    // #i121917#
    bool SdrObject::HasText() const
    {
        return false;
    }

    bool SdrObject::IsTextBox() const
    {
        return false;
    }

    SdrObject* SdrObjFactory::CreateObjectFromFactory(SdrModel& rSdrModel, SdrInventor nInventor, sal_uInt16 nObjIdentifier)
    {
        SdrObjCreatorParams aParams { nInventor, nObjIdentifier, rSdrModel };
        for (const auto & i : ImpGetUserMakeObjHdl()) {
            SdrObject* pObj = i.Call(aParams);
            if (pObj) {
                return pObj;
            }
        }
        return nullptr;
    }

    SdrObject* SdrObjFactory::MakeNewObject(
        SdrModel& rSdrModel,
        SdrInventor nInventor,
        sal_uInt16 nIdentifier,
        const tools::Rectangle* pSnapRect)
    {
        SdrObject* pObj(nullptr);
        bool bSetSnapRect(nullptr != pSnapRect);

        if (nInventor == SdrInventor::Default)
        {
            switch (nIdentifier)
            {
                case OBJ_MEASURE:
                {
                    if(nullptr != pSnapRect)
                    {
                        pObj = new SdrMeasureObj(
                            rSdrModel,
                            pSnapRect->TopLeft(),
                            pSnapRect->BottomRight());
                    }
                    else
                    {
                        pObj = new SdrMeasureObj(rSdrModel);
                    }
                }
                break;
                case OBJ_LINE:
                {
                    if(nullptr != pSnapRect)
                    {
                        basegfx::B2DPolygon aPoly;

                        aPoly.append(
                            basegfx::B2DPoint(
                                pSnapRect->Left(),
                                pSnapRect->Top()));
                        aPoly.append(
                            basegfx::B2DPoint(
                                pSnapRect->Right(),
                                pSnapRect->Bottom()));
                        pObj = new SdrPathObj(
                            rSdrModel,
                            OBJ_LINE,
                            basegfx::B2DPolyPolygon(aPoly));
                    }
                    else
                    {
                        pObj = new SdrPathObj(
                            rSdrModel,
                            OBJ_LINE);
                    }
                }
                break;
                case OBJ_TEXT:
                case OBJ_TITLETEXT:
                case OBJ_OUTLINETEXT:
                {
                    if(nullptr != pSnapRect)
                    {
                        pObj = new SdrRectObj(
                            rSdrModel,
                            static_cast<SdrObjKind>(nIdentifier),
                            *pSnapRect);
                        bSetSnapRect = false;
                    }
                    else
                    {
                        pObj = new SdrRectObj(
                            rSdrModel,
                            static_cast<SdrObjKind>(nIdentifier));
                    }
                }
                break;
                case OBJ_CIRC:
                case OBJ_SECT:
                case OBJ_CARC:
                case OBJ_CCUT:
                {
                    if(nullptr != pSnapRect)
                    {
                        pObj = new SdrCircObj(
                            rSdrModel,
                            static_cast<SdrObjKind>(nIdentifier),
                            *pSnapRect);
                        bSetSnapRect = false;
                    }
                    else
                    {
                        pObj = new SdrCircObj(
                            rSdrModel,
                            static_cast<SdrObjKind>(nIdentifier));
                    }
                }
                break;
                case sal_uInt16(OBJ_NONE       ): pObj=new SdrObject(rSdrModel);                   break;
                case sal_uInt16(OBJ_GRUP       ): pObj=new SdrObjGroup(rSdrModel);                 break;
                case sal_uInt16(OBJ_POLY       ): pObj=new SdrPathObj(rSdrModel, OBJ_POLY       ); break;
                case sal_uInt16(OBJ_PLIN       ): pObj=new SdrPathObj(rSdrModel, OBJ_PLIN       ); break;
                case sal_uInt16(OBJ_PATHLINE   ): pObj=new SdrPathObj(rSdrModel, OBJ_PATHLINE   ); break;
                case sal_uInt16(OBJ_PATHFILL   ): pObj=new SdrPathObj(rSdrModel, OBJ_PATHFILL   ); break;
                case sal_uInt16(OBJ_FREELINE   ): pObj=new SdrPathObj(rSdrModel, OBJ_FREELINE   ); break;
                case sal_uInt16(OBJ_FREEFILL   ): pObj=new SdrPathObj(rSdrModel, OBJ_FREEFILL   ); break;
                case sal_uInt16(OBJ_PATHPOLY   ): pObj=new SdrPathObj(rSdrModel, OBJ_POLY       ); break;
                case sal_uInt16(OBJ_PATHPLIN   ): pObj=new SdrPathObj(rSdrModel, OBJ_PLIN       ); break;
                case sal_uInt16(OBJ_EDGE       ): pObj=new SdrEdgeObj(rSdrModel);                  break;
                case sal_uInt16(OBJ_RECT       ): pObj=new SdrRectObj(rSdrModel);                  break;
                case sal_uInt16(OBJ_GRAF       ): pObj=new SdrGrafObj(rSdrModel);                  break;
                case sal_uInt16(OBJ_OLE2       ): pObj=new SdrOle2Obj(rSdrModel);                  break;
                case sal_uInt16(OBJ_FRAME      ): pObj=new SdrOle2Obj(rSdrModel, true);            break;
                case sal_uInt16(OBJ_CAPTION    ): pObj=new SdrCaptionObj(rSdrModel);               break;
                case sal_uInt16(OBJ_PAGE       ): pObj=new SdrPageObj(rSdrModel);                  break;
                case sal_uInt16(OBJ_UNO        ): pObj=new SdrUnoObj(rSdrModel, OUString());       break;
                case sal_uInt16(OBJ_CUSTOMSHAPE  ): pObj=new SdrObjCustomShape(rSdrModel);       break;
    #if HAVE_FEATURE_AVMEDIA
                case sal_uInt16(OBJ_MEDIA      ): pObj=new SdrMediaObj(rSdrModel);               break;
    #endif
                case sal_uInt16(OBJ_TABLE      ): pObj=new sdr::table::SdrTableObj(rSdrModel);   break;
            }
        }

        if (!pObj)
        {
            pObj = CreateObjectFromFactory(rSdrModel, nInventor, nIdentifier);
        }

        if (!pObj)
        {
            // Well, if no one wants it...
            return nullptr;
        }

        if(bSetSnapRect && nullptr != pSnapRect)
        {
            pObj->SetSnapRect(*pSnapRect);
        }

        return pObj;
    }

    void SdrObjFactory::InsertMakeObjectHdl(Link<SdrObjCreatorParams, SdrObject*> const & rLink)
    {
        std::vector<Link<SdrObjCreatorParams, SdrObject*>>& rLL=ImpGetUserMakeObjHdl();
        auto it = std::find(rLL.begin(), rLL.end(), rLink);
        if (it != rLL.end()) {
            OSL_FAIL("SdrObjFactory::InsertMakeObjectHdl(): Link already in place.");
        } else {
            rLL.push_back(rLink);
        }
    }

    void SdrObjFactory::RemoveMakeObjectHdl(Link<SdrObjCreatorParams, SdrObject*> const & rLink)
    {
        std::vector<Link<SdrObjCreatorParams, SdrObject*>>& rLL=ImpGetUserMakeObjHdl();
        auto it = std::find(rLL.begin(), rLL.end(), rLink);
        if (it != rLL.end())
            rLL.erase(it);
    }

    namespace svx
    {
        ISdrObjectFilter::~ISdrObjectFilter()
        {
        }
    }

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

↑ V547 Expression is always false. Probably the '||' operator should be used here.