diff options
Diffstat (limited to '')
-rw-r--r-- | svx/source/svdraw/svdobj.cxx | 3375 |
1 files changed, 3375 insertions, 0 deletions
diff --git a/svx/source/svdraw/svdobj.cxx b/svx/source/svdraw/svdobj.cxx new file mode 100644 index 000000000..4f13059a7 --- /dev/null +++ b/svx/source/svdraw/svdobj.cxx @@ -0,0 +1,3375 @@ +/* -*- 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 <com/sun/star/frame/XTerminateListener.hpp> +#include <com/sun/star/frame/Desktop.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 <comphelper/processfactory.hxx> +#include <editeng/editeng.hxx> +#include <editeng/outlobj.hxx> +#include <o3tl/deleter.hxx> +#include <math.h> +#include <svl/grabbagitem.hxx> +#include <tools/bigint.hxx> +#include <tools/diagnose_ex.h> +#include <tools/helpers.hxx> +#include <unotools/configmgr.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/ptrstyle.hxx> +#include <vector> + +#include <svx/shapepropertynotifier.hxx> +#include <svx/svdotable.hxx> + +#include <svx/sdr/contact/displayinfo.hxx> +#include <sdr/contact/objectcontactofobjlistpainter.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/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xfltrit.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlntrit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/svdglue.hxx> +#include <svx/svdsob.hxx> +#include <svdobjplusdata.hxx> +#include <svdobjuserdatalist.hxx> + +#include <unordered_set> + +#include <optional> +#include <libxml/xmlwriter.h> +#include <memory> + +#include <svx/scene3d.hxx> +#include <rtl/character.hxx> +#include <tools/UnitConversion.hxx> +#include <o3tl/string_view.hxx> + +using namespace ::com::sun::star; + + +SdrObjUserCall::~SdrObjUserCall() +{ +} + +void SdrObjUserCall::Changed(const SdrObject& /*rObj*/, SdrUserCallType /*eType*/, const tools::Rectangle& /*rOldBoundRect*/) +{ +} + +sal_Int32 SdrObjUserCall::GetPDFAnchorStructureElementId(SdrObject const&, OutputDevice const&) +{ + return -1; +} + +SdrObjMacroHitRec::SdrObjMacroHitRec() : + 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; + std::optional<double> mnRelativeWidth; + std::optional<double> mnRelativeHeight; + sal_Int16 meRelativeWidthRelation; + sal_Int16 meRelativeHeightRelation; + + Impl() : + meRelativeWidthRelation(text::RelOrientation::PAGE_FRAME), + meRelativeHeightRelation(text::RelOrientation::PAGE_FRAME) {} +}; + +const std::shared_ptr< svx::diagram::IDiagramHelper >& SdrObject::getDiagramHelper() const +{ + static std::shared_ptr< svx::diagram::IDiagramHelper > aEmpty; + return aEmpty; +} + +// BaseProperties section + +std::unique_ptr<sdr::properties::BaseProperties> SdrObject::CreateObjectSpecificProperties() +{ + return std::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 std::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 (SdrObjList* pParentList = getParentSdrObjListFromSdrObject()) + { + return pParentList->getSdrPageFromSdrObjList(); + } + + return nullptr; +} + +SdrModel& SdrObject::getSdrModelFromSdrObject() const +{ + return mrSdrModelFromSdrObject; +} + +void SdrObject::setParentOfSdrObject(SdrObjList* pNewObjList) +{ + if(getParentSdrObjListFromSdrObject() == pNewObjList) + return; + + // 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() +{ + m_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) + ,m_pUserCall(nullptr) + ,mpImpl(new Impl) + ,mpParentOfSdrObject(nullptr) + ,m_nOrdNum(0) + ,mnNavigationPosition(SAL_MAX_UINT32) + ,mnLayerID(0) + ,mpSvxShape( nullptr ) + ,mbDoNotInsertIntoPageAutomatically(false) +{ + m_bVirtObj =false; + m_bSnapRectDirty =true; + m_bMovProt =false; + m_bSizProt =false; + m_bNoPrint =false; + m_bEmptyPresObj =false; + m_bNotVisibleAsMaster=false; + m_bClosedObj =false; + mbVisible = true; + + // #i25616# + mbLineIsOutsideGeometry = false; + + // #i25616# + mbSupportTextIndentingOnLineWidthChange = false; + + m_bIsEdge=false; + m_bIs3DObj=false; + m_bMarkProt=false; + m_bIsUnoObj=false; +#ifdef DBG_UTIL + // SdrObjectLifetimeWatchDog: + impAddIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject()); +#endif +} + +SdrObject::SdrObject(SdrModel& rSdrModel, SdrObject const & rSource) +: mpFillGeometryDefiningShape(nullptr) + ,mrSdrModelFromSdrObject(rSdrModel) + ,m_pUserCall(nullptr) + ,mpImpl(new Impl) + ,mpParentOfSdrObject(nullptr) + ,m_nOrdNum(0) + ,mnNavigationPosition(SAL_MAX_UINT32) + ,mnLayerID(0) + ,mpSvxShape( nullptr ) + ,mbDoNotInsertIntoPageAutomatically(false) +{ + m_bVirtObj =false; + m_bSnapRectDirty =true; + m_bMovProt =false; + m_bSizProt =false; + m_bNoPrint =false; + m_bEmptyPresObj =false; + m_bNotVisibleAsMaster=false; + m_bClosedObj =false; + mbVisible = true; + + // #i25616# + mbLineIsOutsideGeometry = false; + + // #i25616# + mbSupportTextIndentingOnLineWidthChange = false; + + m_bIsEdge=false; + m_bIs3DObj=false; + m_bMarkProt=false; + m_bIsUnoObj=false; +#ifdef DBG_UTIL + // SdrObjectLifetimeWatchDog: + impAddIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject()); +#endif + + 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 = rSource.GetProperties().Clone(*this); + + m_aOutRect=rSource.m_aOutRect; + mnLayerID = rSource.mnLayerID; + m_aAnchor =rSource.m_aAnchor; + m_bVirtObj=rSource.m_bVirtObj; + m_bSizProt=rSource.m_bSizProt; + m_bMovProt=rSource.m_bMovProt; + m_bNoPrint=rSource.m_bNoPrint; + mbVisible=rSource.mbVisible; + m_bMarkProt=rSource.m_bMarkProt; + m_bEmptyPresObj =rSource.m_bEmptyPresObj; + m_bNotVisibleAsMaster=rSource.m_bNotVisibleAsMaster; + m_bSnapRectDirty=true; + m_pPlusData.reset(); + if (rSource.m_pPlusData!=nullptr) { + m_pPlusData.reset(rSource.m_pPlusData->Clone(this)); + } + if (m_pPlusData!=nullptr && m_pPlusData->pBroadcast!=nullptr) { + m_pPlusData->pBroadcast.reset(); // broadcaster isn't copied + } + + m_pGrabBagItem.reset(); + if (rSource.m_pGrabBagItem!=nullptr) + m_pGrabBagItem.reset(rSource.m_pGrabBagItem->Clone()); +} + +SdrObject::~SdrObject() +{ + // Tell all the registered ObjectUsers that the page is in destruction. + // And clear the vector. This means that user do not need to call RemoveObjectUser() + // when they get called from ObjectInDestruction(). + sdr::ObjectUserVector aList; + aList.swap(mpImpl->maObjectUsers); + for(sdr::ObjectUser* pObjectUser : aList) + { + DBG_ASSERT(pObjectUser, "SdrObject::~SdrObject: corrupt ObjectUser list (!)"); + pObjectUser->ObjectInDestruction(*this); + } + + // UserCall + SendUserCall(SdrUserCallType::Delete, GetLastBoundRect()); + o3tl::reset_preserve_ptr_during(m_pPlusData); + + m_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().get(), uno::UNO_QUERY_THROW ); + xShapeComp->dispose(); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + } + + delete pObject; +} + +void SdrObject::SetBoundAndSnapRectsDirty(bool bNotMyself, bool bRecursive) +{ + if (!bNotMyself) + { + SetBoundRectDirty(); + m_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); + } + } +} + + +// global static ItemPool for not-yet-inserted items +static rtl::Reference<SdrItemPool> mpGlobalItemPool; + +/** If we let the libc runtime clean us up, we trigger a crash */ +namespace +{ +class TerminateListener : public ::cppu::WeakImplHelper< css::frame::XTerminateListener > +{ + void SAL_CALL queryTermination( const lang::EventObject& ) override + {} + void SAL_CALL notifyTermination( const lang::EventObject& ) override + { + mpGlobalItemPool.clear(); + } + virtual void SAL_CALL disposing( const ::css::lang::EventObject& ) override + {} +}; +}; + +// init global static itempool +SdrItemPool& SdrObject::GetGlobalDrawObjectItemPool() +{ + if(!mpGlobalItemPool) + { + mpGlobalItemPool = new SdrItemPool(); + rtl::Reference<SfxItemPool> pGlobalOutlPool = EditEngine::CreatePool(); + mpGlobalItemPool->SetSecondaryPool(pGlobalOutlPool.get()); + mpGlobalItemPool->SetDefaultMetric(SdrEngineDefaults::GetMapUnit()); + mpGlobalItemPool->FreezeIdRanges(); + if (utl::ConfigManager::IsFuzzing()) + mpGlobalItemPool->acquire(); + else + { + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext()); + uno::Reference< frame::XTerminateListener > xListener( new TerminateListener ); + xDesktop->addTerminateListener( xListener ); + } + } + + return *mpGlobalItemPool; +} + +void SdrObject::SetRelativeWidth( double nValue ) +{ + mpImpl->mnRelativeWidth = nValue; +} + +void SdrObject::SetRelativeWidthRelation( sal_Int16 eValue ) +{ + mpImpl->meRelativeWidthRelation = eValue; +} + +void SdrObject::SetRelativeHeight( double nValue ) +{ + mpImpl->mnRelativeHeight = nValue; +} + +void SdrObject::SetRelativeHeightRelation( sal_Int16 eValue ) +{ + mpImpl->meRelativeHeightRelation = eValue; +} + +const double* SdrObject::GetRelativeWidth( ) const +{ + if (!mpImpl->mnRelativeWidth) + return nullptr; + + return &*mpImpl->mnRelativeWidth; +} + +sal_Int16 SdrObject::GetRelativeWidthRelation() const +{ + return mpImpl->meRelativeWidthRelation; +} + +const double* SdrObject::GetRelativeHeight( ) const +{ + if (!mpImpl->mnRelativeHeight) + return nullptr; + + return &*mpImpl->mnRelativeHeight; +} + +sal_Int16 SdrObject::GetRelativeHeightRelation() const +{ + return mpImpl->meRelativeHeightRelation; +} + +SfxItemPool& SdrObject::GetObjectItemPool() const +{ + return getSdrModelFromSdrObject().GetItemPool(); +} + +SdrInventor SdrObject::GetObjInventor() const +{ + return SdrInventor::Default; +} + +SdrObjKind SdrObject::GetObjIdentifier() const +{ + return SdrObjKind::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 (m_pPlusData->pBroadcast==nullptr) m_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(*m_pPlusData->pBroadcast, pEdge ? DuplicateHandling::Allow : DuplicateHandling::Unexpected); +} + +void SdrObject::RemoveListener(SfxListener& rListener) +{ + if (m_pPlusData!=nullptr && m_pPlusData->pBroadcast!=nullptr) { + rListener.EndListening(*m_pPlusData->pBroadcast); + if (!m_pPlusData->pBroadcast->HasListeners()) { + m_pPlusData->pBroadcast.reset(); + } + } +} + +const SfxBroadcaster* SdrObject::GetBroadcaster() const +{ + return m_pPlusData!=nullptr ? m_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, const bool bSetChanged) +{ + if (!rStr.isEmpty() && !m_pPlusData) + { + ImpForcePlusData(); + } + + if(!(m_pPlusData && m_pPlusData->aObjName != rStr)) + return; + + // Undo/Redo for setting object's name (#i73249#) + bool bUndo( false ); + if ( getSdrModelFromSdrObject().IsUndoEnabled() ) + { + bUndo = true; + std::unique_ptr<SdrUndoAction> pUndoAction = + SdrUndoFactory::CreateUndoObjectStrAttr( + *this, + SdrUndoObjStrAttr::ObjStrAttrType::Name, + GetName(), + rStr ); + getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() ); + getSdrModelFromSdrObject().AddUndo( std::move(pUndoAction) ); + } + m_pPlusData->aObjName = rStr; + // Undo/Redo for setting object's name (#i73249#) + if ( bUndo ) + { + getSdrModelFromSdrObject().EndUndo(); + } + if (bSetChanged) + { + SetChanged(); + BroadcastObjectChange(); + } +} + +const OUString & SdrObject::GetName() const +{ + static const OUString EMPTY = u""; + + if(m_pPlusData) + { + return m_pPlusData->aObjName; + } + + return EMPTY; +} + +void SdrObject::SetTitle(const OUString& rStr) +{ + if (!rStr.isEmpty() && !m_pPlusData) + { + ImpForcePlusData(); + } + + if(!(m_pPlusData && m_pPlusData->aObjTitle != rStr)) + return; + + // Undo/Redo for setting object's title (#i73249#) + bool bUndo( false ); + if ( getSdrModelFromSdrObject().IsUndoEnabled() ) + { + bUndo = true; + std::unique_ptr<SdrUndoAction> pUndoAction = + SdrUndoFactory::CreateUndoObjectStrAttr( + *this, + SdrUndoObjStrAttr::ObjStrAttrType::Title, + GetTitle(), + rStr ); + getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() ); + getSdrModelFromSdrObject().AddUndo( std::move(pUndoAction) ); + } + m_pPlusData->aObjTitle = rStr; + // Undo/Redo for setting object's title (#i73249#) + if ( bUndo ) + { + getSdrModelFromSdrObject().EndUndo(); + } + SetChanged(); + BroadcastObjectChange(); +} + +OUString SdrObject::GetTitle() const +{ + if(m_pPlusData) + { + return m_pPlusData->aObjTitle; + } + + return OUString(); +} + +void SdrObject::SetDescription(const OUString& rStr) +{ + if (!rStr.isEmpty() && !m_pPlusData) + { + ImpForcePlusData(); + } + + if(!(m_pPlusData && m_pPlusData->aObjDescription != rStr)) + return; + + // Undo/Redo for setting object's description (#i73249#) + bool bUndo( false ); + if ( getSdrModelFromSdrObject().IsUndoEnabled() ) + { + bUndo = true; + std::unique_ptr<SdrUndoAction> pUndoAction = + SdrUndoFactory::CreateUndoObjectStrAttr( + *this, + SdrUndoObjStrAttr::ObjStrAttrType::Description, + GetDescription(), + rStr ); + getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() ); + getSdrModelFromSdrObject().AddUndo( std::move(pUndoAction) ); + } + m_pPlusData->aObjDescription = rStr; + // Undo/Redo for setting object's description (#i73249#) + if ( bUndo ) + { + getSdrModelFromSdrObject().EndUndo(); + } + SetChanged(); + BroadcastObjectChange(); +} + +OUString SdrObject::GetDescription() const +{ + if(m_pPlusData) + { + return m_pPlusData->aObjDescription; + } + + return OUString(); +} + +sal_uInt32 SdrObject::GetOrdNum() const +{ + if (SdrObjList* pParentList = getParentSdrObjListFromSdrObject()) + { + if (pParentList->IsObjOrdNumsDirty()) + { + pParentList->RecalcObjOrdNums(); + } + } else const_cast<SdrObject*>(this)->m_nOrdNum=0; + return m_nOrdNum; +} + +void SdrObject::SetOrdNum(sal_uInt32 nNum) +{ + m_nOrdNum = nNum; +} + +void SdrObject::GetGrabBagItem(css::uno::Any& rVal) const +{ + if (m_pGrabBagItem != nullptr) + m_pGrabBagItem->QueryValue(rVal); + else + rVal <<= uno::Sequence<beans::PropertyValue>(); +} + +void SdrObject::SetGrabBagItem(const css::uno::Any& rVal) +{ + if (m_pGrabBagItem == nullptr) + m_pGrabBagItem.reset(new SfxGrabBagItem); + + m_pGrabBagItem->PutValue(rVal, 0); + + SetChanged(); + BroadcastObjectChange(); +} + +sal_uInt32 SdrObject::GetNavigationPosition() const +{ + 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(m_aOutRect.IsEmpty()) + { + const_cast< SdrObject* >(this)->RecalcBoundRect(); + } + + return m_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 m_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(!m_aOutRect.IsEmpty()) + return; + + // Use view-independent data - we do not want any connections + // to e.g. GridOffset in SdrObject-level + drawinglayer::primitive2d::Primitive2DContainer xPrimitives; + GetViewContact().getViewIndependentPrimitive2DContainer(xPrimitives); + + if(xPrimitives.empty()) + return; + + // 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()) + { + m_aOutRect = tools::Rectangle( + static_cast<tools::Long>(floor(aRange.getMinX())), + static_cast<tools::Long>(floor(aRange.getMinY())), + static_cast<tools::Long>(ceil(aRange.getMaxX())), + static_cast<tools::Long>(ceil(aRange.getMaxY()))); + return; + } +} + +void SdrObject::BroadcastObjectChange() const +{ + if ((getSdrModelFromSdrObject().isLocked()) || utl::ConfigManager::IsFuzzing()) + return; + + bool bPlusDataBroadcast(m_pPlusData && m_pPlusData->pBroadcast); + bool bObjectChange(IsInserted()); + + if(!(bPlusDataBroadcast || bObjectChange)) + return; + + SdrHint aHint(SdrHintKind::ObjectChange, *this); + + if(bPlusDataBroadcast) + { + m_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 (?) + // TTTT tdf#120066 Indeed - This triggers e.g. by CustomShape + // geometry-presenting SdrObjects that are in a SdrObjGroup, + // but the SdrObjGroup is *by purpose* not inserted. + // Need to check deeper and maybe identify all ::IsInserted() + // calls by rename and let the compiler work... + if(nullptr != getSdrPageFromSdrObject()) + { + 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, std::move(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 new SdrObject(rTargetModel, *this); +} + +OUString SdrObject::TakeObjNameSingul() const +{ + OUString sName(SvxResId(STR_ObjNameSingulNONE)); + + OUString aName(GetName()); + if (!aName.isEmpty()) + sName += " '" + aName + "'"; + return sName; +} + +OUString SdrObject::TakeObjNamePlural() const +{ + return SvxResId(STR_ObjNamePluralNONE); +} + +OUString SdrObject::ImpGetDescriptionStr(TranslateId pStrCacheID) const +{ + OUString aStr = SvxResId(pStrCacheID); + sal_Int32 nPos = aStr.indexOf("%1"); + if (nPos >= 0) + { + // Replace '%1' with the object name. + OUString aObjName(TakeObjNameSingul()); + aStr = aStr.replaceAt(nPos, 2, aObjName); + } + + nPos = aStr.indexOf("%2"); + if (nPos >= 0) + // Replace '%2' with the passed value. + aStr = aStr.replaceAt(nPos, 2, u"0"); + return aStr; +} + +void SdrObject::ImpForcePlusData() +{ + if (!m_pPlusData) + m_pPlusData.reset( new SdrObjPlusData ); +} + +OUString SdrObject::GetMetrStr(tools::Long nVal) const +{ + return getSdrModelFromSdrObject().GetMetricString(nVal); +} + +basegfx::B2DPolyPolygon SdrObject::TakeXorPoly() const +{ + basegfx::B2DPolyPolygon aRetval; + const tools::Rectangle aR(GetCurrentBoundRect()); + aRetval.append(basegfx::utils::createPolygonFromRect(vcl::unotools::b2DRectangleFromRectangle(aR))); + + 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(std::nullopt); + } + + 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()); + drawinglayer::primitive2d::Primitive2DContainer xSequence; + rVC.getViewIndependentPrimitive2DContainer(xSequence); + + 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 8; +} + +void SdrObject::AddToHdlList(SdrHdlList& rHdlList) const +{ + const tools::Rectangle& rR=GetSnapRect(); + for (sal_uInt32 nHdlNum=0; nHdlNum<8; ++nHdlNum) + { + std::unique_ptr<SdrHdl> pH; + switch (nHdlNum) { + case 0: pH.reset(new SdrHdl(rR.TopLeft(), SdrHdlKind::UpperLeft)); break; + case 1: pH.reset(new SdrHdl(rR.TopCenter(), SdrHdlKind::Upper)); break; + case 2: pH.reset(new SdrHdl(rR.TopRight(), SdrHdlKind::UpperRight)); break; + case 3: pH.reset(new SdrHdl(rR.LeftCenter(), SdrHdlKind::Left )); break; + case 4: pH.reset(new SdrHdl(rR.RightCenter(), SdrHdlKind::Right)); break; + case 5: pH.reset(new SdrHdl(rR.BottomLeft(), SdrHdlKind::LowerLeft)); break; + case 6: pH.reset(new SdrHdl(rR.BottomCenter(),SdrHdlKind::Lower)); break; + case 7: pH.reset(new SdrHdl(rR.BottomRight(), SdrHdlKind::LowerRight)); break; + } + rHdlList.AddHdl(std::move(pH)); + } +} + +void SdrObject::AddToPlusHdlList(SdrHdlList&, SdrHdl&) const +{ +} + +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 + tools::Long nWdt0=aRect.Right() -aRect.Left(); + tools::Long nHgt0=aRect.Bottom()-aRect.Top(); + tools::Long nXMul=aTmpRect.Right() -aTmpRect.Left(); + tools::Long nYMul=aTmpRect.Bottom()-aTmpRect.Top(); + tools::Long nXDiv=nWdt0; + tools::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) { + tools::Long nNeed=tools::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 { + tools::Long nNeed=tools::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) { + tools::Long nHgt0b=aRect.Bottom()-aRect.Top(); + tools::Long nNeed=tools::Long(BigInt(nHgt0b)*BigInt(nXMul)/BigInt(nXDiv)); + aTmpRect.AdjustTop( -((nNeed-nHgt0b)/2) ); + aTmpRect.SetBottom(aTmpRect.Top()+nNeed ); + } + if ((bTop || bBtm) && nYDiv!=0) { + tools::Long nWdt0b=aRect.Right()-aRect.Left(); + tools::Long nNeed=tools::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; +} + +SdrObjectUniquePtr SdrObject::getFullDragClone() const +{ + // default uses simple clone + return SdrObjectUniquePtr(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); + m_aOutRect = aRect1; + return true; +} + +bool SdrObject::MovCreate(SdrDragStat& rStat) +{ + rStat.TakeCreateRect(m_aOutRect); + rStat.SetActionRect(m_aOutRect); + m_aOutRect.Justify(); + + return true; +} + +bool SdrObject::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd) +{ + rStat.TakeCreateRect(m_aOutRect); + m_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; + aRetval.append(basegfx::utils::createPolygonFromRect(vcl::unotools::b2DRectangleFromRectangle(aRect1))); + return aRetval; +} + +PointerStyle SdrObject::GetCreatePointer() const +{ + return PointerStyle::Cross; +} + +// transformations +void SdrObject::NbcMove(const Size& rSiz) +{ + m_aOutRect.Move(rSiz); + SetBoundAndSnapRectsDirty(); +} + +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(m_aOutRect,rRef,xFact,yFact); + SetBoundAndSnapRectsDirty(); +} + +void SdrObject::NbcRotate(const Point& rRef, Degree100 nAngle) +{ + if (nAngle) + { + double a = toRadians(nAngle); + NbcRotate( rRef, nAngle, sin( a ), cos( a ) ); + } +} + +void SdrObject::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs) +{ + SetGlueReallyAbsolute(true); + m_aOutRect.Move(-rRef.X(),-rRef.Y()); + tools::Rectangle R(m_aOutRect); + if (sn==1.0 && cs==0.0) { // 90deg + m_aOutRect.SetLeft(-R.Bottom() ); + m_aOutRect.SetRight(-R.Top() ); + m_aOutRect.SetTop(R.Left() ); + m_aOutRect.SetBottom(R.Right() ); + } else if (sn==0.0 && cs==-1.0) { // 180deg + m_aOutRect.SetLeft(-R.Right() ); + m_aOutRect.SetRight(-R.Left() ); + m_aOutRect.SetTop(-R.Bottom() ); + m_aOutRect.SetBottom(-R.Top() ); + } else if (sn==-1.0 && cs==0.0) { // 270deg + m_aOutRect.SetLeft(R.Top() ); + m_aOutRect.SetRight(R.Bottom() ); + m_aOutRect.SetTop(-R.Right() ); + m_aOutRect.SetBottom(-R.Left() ); + } + m_aOutRect.Move(rRef.X(),rRef.Y()); + m_aOutRect.Justify(); // just in case + SetBoundAndSnapRectsDirty(); + NbcRotateGluePoints(rRef,nAngle,sn,cs); + SetGlueReallyAbsolute(false); +} + +void SdrObject::NbcMirror(const Point& rRef1, const Point& rRef2) +{ + SetGlueReallyAbsolute(true); + m_aOutRect.Move(-rRef1.X(),-rRef1.Y()); + tools::Rectangle R(m_aOutRect); + tools::Long dx=rRef2.X()-rRef1.X(); + tools::Long dy=rRef2.Y()-rRef1.Y(); + if (dx==0) { // vertical axis + m_aOutRect.SetLeft(-R.Right() ); + m_aOutRect.SetRight(-R.Left() ); + } else if (dy==0) { // horizontal axis + m_aOutRect.SetTop(-R.Bottom() ); + m_aOutRect.SetBottom(-R.Top() ); + } else if (dx==dy) { // 45deg axis + m_aOutRect.SetLeft(R.Top() ); + m_aOutRect.SetRight(R.Bottom() ); + m_aOutRect.SetTop(R.Left() ); + m_aOutRect.SetBottom(R.Right() ); + } else if (dx==-dy) { // 45deg axis + m_aOutRect.SetLeft(-R.Bottom() ); + m_aOutRect.SetRight(-R.Top() ); + m_aOutRect.SetTop(-R.Right() ); + m_aOutRect.SetBottom(-R.Left() ); + } + m_aOutRect.Move(rRef1.X(),rRef1.Y()); + m_aOutRect.Justify(); // just in case + SetBoundAndSnapRectsDirty(); + NbcMirrorGluePoints(rRef1,rRef2); + SetGlueReallyAbsolute(false); +} + +void SdrObject::NbcShear(const Point& rRef, Degree100 /*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 (m_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()) + return; + + if (bUnsetRelative) + { + mpImpl->mnRelativeWidth.reset(); + mpImpl->meRelativeWidthRelation = text::RelOrientation::PAGE_FRAME; + mpImpl->meRelativeHeightRelation = text::RelOrientation::PAGE_FRAME; + mpImpl->mnRelativeHeight.reset(); + } + tools::Rectangle aBoundRect0; if (m_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 (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect(); + NbcCrop(rRef, fxFact, fyFact); + SetChanged(); + BroadcastObjectChange(); + SendUserCall(SdrUserCallType::Resize,aBoundRect0); +} + +void SdrObject::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs) +{ + if (nAngle) { + tools::Rectangle aBoundRect0; if (m_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 (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect(); + NbcMirror(rRef1,rRef2); + SetChanged(); + BroadcastObjectChange(); + SendUserCall(SdrUserCallType::Resize,aBoundRect0); +} + +void SdrObject::Shear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear) +{ + if (nAngle) { + tools::Rectangle aBoundRect0; if (m_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()-m_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 (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect(); + NbcSetRelativePos(rPnt); + SetChanged(); + BroadcastObjectChange(); + SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0); + } +} + +Point SdrObject::GetRelativePos() const +{ + return GetSnapRect().TopLeft()-m_aAnchor; +} + +void SdrObject::ImpSetAnchorPos(const Point& rPnt) +{ + m_aAnchor = rPnt; +} + +void SdrObject::NbcSetAnchorPos(const Point& rPnt) +{ + Size aSiz(rPnt.X()-m_aAnchor.X(),rPnt.Y()-m_aAnchor.Y()); + m_aAnchor=rPnt; + NbcMove(aSiz); // This also calls SetRectsDirty() +} + +void SdrObject::SetAnchorPos(const Point& rPnt) +{ + if (rPnt!=m_aAnchor) { + tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect(); + NbcSetAnchorPos(rPnt); + SetChanged(); + BroadcastObjectChange(); + SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0); + } +} + +const Point& SdrObject::GetAnchorPos() const +{ + return m_aAnchor; +} + +void SdrObject::RecalcSnapRect() +{ +} + +const tools::Rectangle& SdrObject::GetSnapRect() const +{ + return m_aOutRect; +} + +void SdrObject::NbcSetSnapRect(const tools::Rectangle& rRect) +{ + m_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 (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect(); + NbcSetSnapRect(rRect); + SetChanged(); + BroadcastObjectChange(); + SendUserCall(SdrUserCallType::Resize,aBoundRect0); +} + +void SdrObject::SetLogicRect(const tools::Rectangle& rRect) +{ + tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect(); + NbcSetLogicRect(rRect); + SetChanged(); + BroadcastObjectChange(); + SendUserCall(SdrUserCallType::Resize,aBoundRect0); +} + +Degree100 SdrObject::GetRotateAngle() const +{ + return 0_deg100; +} + +Degree100 SdrObject::GetShearAngle(bool /*bVertical*/) const +{ + return 0_deg100; +} + +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 0; +} + +Point SdrObject::GetPoint(sal_uInt32 /*i*/) const +{ + return Point(); +} + +void SdrObject::SetPoint(const Point& rPnt, sal_uInt32 i) +{ + tools::Rectangle aBoundRect0; if (m_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 (m_aAnchor.X() == rOtherObj.m_aAnchor.X() && m_aAnchor.Y() == rOtherObj.m_aAnchor.Y() && + m_nOrdNum == rOtherObj.m_nOrdNum && mnNavigationPosition == rOtherObj.mnNavigationPosition && + mbSupportTextIndentingOnLineWidthChange == rOtherObj.mbSupportTextIndentingOnLineWidthChange && + mbLineIsOutsideGeometry == rOtherObj.mbLineIsOutsideGeometry && m_bMarkProt == rOtherObj.m_bMarkProt && + m_bIs3DObj == rOtherObj.m_bIs3DObj && m_bIsEdge == rOtherObj.m_bIsEdge && m_bClosedObj == rOtherObj.m_bClosedObj && + m_bNotVisibleAsMaster == rOtherObj.m_bNotVisibleAsMaster && m_bEmptyPresObj == rOtherObj.m_bEmptyPresObj && + mbVisible == rOtherObj.mbVisible && m_bNoPrint == rOtherObj.m_bNoPrint && m_bSizProt == rOtherObj.m_bSizProt && + m_bMovProt == rOtherObj.m_bMovProt && m_bVirtObj == rOtherObj.m_bVirtObj && + mnLayerID == rOtherObj.mnLayerID && GetMergedItemSet().Equals(rOtherObj.GetMergedItemSet(), false) ); +} + +void SdrObject::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrObject")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name())); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("name"), "%s", BAD_CAST(GetName().toUtf8().getStr())); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("title"), "%s", BAD_CAST(GetTitle().toUtf8().getStr())); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("description"), "%s", BAD_CAST(GetDescription().toUtf8().getStr())); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("nOrdNum"), "%" SAL_PRIuUINT32, GetOrdNumDirect()); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aOutRect"), BAD_CAST(m_aOutRect.toString().getStr())); + + if (m_pGrabBagItem) + { + m_pGrabBagItem->dumpAsXml(pWriter); + } + + if (mpProperties) + { + mpProperties->dumpAsXml(pWriter); + } + + if (const OutlinerParaObject* pOutliner = GetOutlinerParaObject()) + pOutliner->dumpAsXml(pWriter); + + (void)xmlTextWriterEndElement(pWriter); +} + +void SdrObject::SetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject) +{ + tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect(); + NbcSetOutlinerParaObject(std::move(pTextObject)); + SetChanged(); + BroadcastObjectChange(); + if (GetCurrentBoundRect()!=aBoundRect0) { + SendUserCall(SdrUserCallType::Resize,aBoundRect0); + } + + if (!getSdrModelFromSdrObject().IsUndoEnabled()) + return; + + // Don't do this during import. + SdrObject* pTopGroupObj = nullptr; + if (getParentSdrObjectFromSdrObject()) + { + pTopGroupObj = getParentSdrObjectFromSdrObject(); + while (pTopGroupObj->getParentSdrObjectFromSdrObject()) + { + pTopGroupObj = pTopGroupObj->getParentSdrObjectFromSdrObject(); + } + } + if (pTopGroupObj) + { + // A shape was modified, which is in a group shape. Empty the group shape's grab-bag, + // which potentially contains the old text of the shapes in case of diagrams. + pTopGroupObj->SetGrabBagItem(uno::Any(uno::Sequence<beans::PropertyValue>())); + } +} + +void SdrObject::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> /*pTextObject*/) +{ +} + +OutlinerParaObject* SdrObject::GetOutlinerParaObject() const +{ + return nullptr; +} + +void SdrObject::NbcReformatText() +{ +} + +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; +} + +PointerStyle SdrObject::GetMacroPointer(const SdrObjMacroHitRec&) const +{ + return PointerStyle::RefHand; +} + +void SdrObject::PaintMacro(OutputDevice& rOut, const tools::Rectangle& , const SdrObjMacroHitRec& ) const +{ + const RasterOp eRop(rOut.GetRasterOp()); + const basegfx::B2DPolyPolygon aPolyPolygon(TakeXorPoly()); + + rOut.SetLineColor(COL_BLACK); + rOut.SetFillColor(); + rOut.SetRasterOp(RasterOp::Invert); + + for(auto const& rPolygon : aPolyPolygon) + { + rOut.DrawPolyLine(rPolygon); + } + + rOut.SetRasterOp(eRop); +} + +bool SdrObject::DoMacro(const SdrObjMacroHitRec&) +{ + return false; +} + +bool SdrObject::IsMacroHit(const SdrObjMacroHitRec& rRec) const +{ + return CheckMacroHit(rRec) != nullptr; +} + + +std::unique_ptr<SdrObjGeoData> SdrObject::NewGeoData() const +{ + return std::make_unique<SdrObjGeoData>(); +} + +void SdrObject::SaveGeoData(SdrObjGeoData& rGeo) const +{ + rGeo.aBoundRect =GetCurrentBoundRect(); + rGeo.aAnchor =m_aAnchor ; + rGeo.bMovProt =m_bMovProt ; + rGeo.bSizProt =m_bSizProt ; + rGeo.bNoPrint =m_bNoPrint ; + rGeo.mbVisible =mbVisible ; + rGeo.bClosedObj =m_bClosedObj ; + rGeo.mnLayerID = mnLayerID; + + // user-defined gluepoints + if (m_pPlusData!=nullptr && m_pPlusData->pGluePoints!=nullptr) { + if (rGeo.pGPL!=nullptr) { + *rGeo.pGPL=*m_pPlusData->pGluePoints; + } else { + rGeo.pGPL.reset( new SdrGluePointList(*m_pPlusData->pGluePoints) ); + } + } else { + rGeo.pGPL.reset(); + } +} + +void SdrObject::RestoreGeoData(const SdrObjGeoData& rGeo) +{ + SetBoundAndSnapRectsDirty(); + m_aOutRect =rGeo.aBoundRect ; + m_aAnchor =rGeo.aAnchor ; + m_bMovProt =rGeo.bMovProt ; + m_bSizProt =rGeo.bSizProt ; + m_bNoPrint =rGeo.bNoPrint ; + mbVisible =rGeo.mbVisible ; + m_bClosedObj =rGeo.bClosedObj ; + mnLayerID = rGeo.mnLayerID; + + // user-defined gluepoints + if (rGeo.pGPL!=nullptr) { + ImpForcePlusData(); + if (m_pPlusData->pGluePoints!=nullptr) { + *m_pPlusData->pGluePoints=*rGeo.pGPL; + } else { + m_pPlusData->pGluePoints.reset(new SdrGluePointList(*rGeo.pGPL)); + } + } else { + if (m_pPlusData!=nullptr && m_pPlusData->pGluePoints!=nullptr) { + m_pPlusData->pGluePoints.reset(); + } + } +} + +std::unique_ptr<SdrObjGeoData> SdrObject::GetGeoData() const +{ + std::unique_ptr<SdrObjGeoData> pGeo = NewGeoData(); + SaveGeoData(*pGeo); + return pGeo; +} + +void SdrObject::SetGeoData(const SdrObjGeoData& rGeo) +{ + tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect(); + RestoreGeoData(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 (m_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()); + + if (const SdrTransformRef1XItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF1X)) + { + aRef1.setX(pPoolItem->GetValue() ); + } + if (const SdrTransformRef1YItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF1Y)) + { + aRef1.setY(pPoolItem->GetValue() ); + } + + tools::Rectangle aNewSnap(rSnap); + if (const SdrMoveXItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_MOVEX)) + { + tools::Long n = pPoolItem->GetValue(); + aNewSnap.Move(n,0); + } + if (const SdrMoveYItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_MOVEY)) + { + tools::Long n = pPoolItem->GetValue(); + aNewSnap.Move(0,n); + } + if (const SdrOnePositionXItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONEPOSITIONX)) + { + tools::Long n = pPoolItem->GetValue(); + aNewSnap.Move(n-aNewSnap.Left(),0); + } + if (const SdrOnePositionYItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONEPOSITIONY)) + { + tools::Long n = pPoolItem->GetValue(); + aNewSnap.Move(0,n-aNewSnap.Top()); + } + if (const SdrOneSizeWidthItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONESIZEWIDTH)) + { + tools::Long n = pPoolItem->GetValue(); + aNewSnap.SetRight(aNewSnap.Left()+n ); + } + if (const SdrOneSizeHeightItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONESIZEHEIGHT)) + { + tools::Long n = 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 (const SdrShearAngleItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_SHEARANGLE)) + { + Degree100 n = pPoolItem->GetValue(); + n-=GetShearAngle(); + if (n) { + double nTan = tan(toRadians(n)); + NbcShear(aRef1,n,nTan,false); + } + } + if (const SdrAngleItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ROTATEANGLE)) + { + Degree100 n = pPoolItem->GetValue(); + n-=GetRotateAngle(); + if (n) { + NbcRotate(aRef1,n); + } + } + if (const SdrRotateOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ROTATEONE)) + { + Degree100 n = pPoolItem->GetValue(); + NbcRotate(aRef1,n); + } + if (const SdrHorzShearOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_HORZSHEARONE)) + { + Degree100 n = pPoolItem->GetValue(); + double nTan = tan(toRadians(n)); + NbcShear(aRef1,n,nTan,false); + } + if (const SdrVertShearOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_VERTSHEARONE)) + { + Degree100 n = pPoolItem->GetValue(); + double nTan = tan(toRadians(n)); + NbcShear(aRef1,n,nTan,true); + } + + if (const SdrYesNoItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJMOVEPROTECT)) + { + bool b = pPoolItem->GetValue(); + SetMoveProtect(b); + } + if (const SdrYesNoItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJSIZEPROTECT)) + { + bool b = pPoolItem->GetValue(); + SetResizeProtect(b); + } + + /* move protect always sets size protect */ + if( IsMoveProtect() ) + SetResizeProtect( true ); + + if (const SdrObjPrintableItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJPRINTABLE)) + { + bool b = pPoolItem->GetValue(); + SetPrintable(b); + } + + if (const SdrObjVisibleItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJVISIBLE)) + { + bool b = pPoolItem->GetValue(); + SetVisible(b); + } + + SdrLayerID nLayer=SDRLAYER_NOTFOUND; + if (const SdrLayerIdItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LAYERID)) + { + nLayer = pPoolItem->GetValue(); + } + if (const SdrLayerNameItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LAYERNAME)) + { + OUString aLayerName = 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 (const SfxStringItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJECTNAME)) + { + OUString aName = pPoolItem->GetValue(); + SetName(aName); + } + tools::Rectangle aNewLogic(rLogic); + if (const SdrLogicSizeWidthItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LOGICSIZEWIDTH)) + { + tools::Long n = pPoolItem->GetValue(); + aNewLogic.SetRight(aNewLogic.Left()+n ); + } + if (const SdrLogicSizeHeightItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LOGICSIZEHEIGHT)) + { + tools::Long n = pPoolItem->GetValue(); + aNewLogic.SetBottom(aNewLogic.Top()+n ); + } + if (aNewLogic!=rLogic) { + NbcSetLogicRect(aNewLogic); + } + Fraction aResizeX(1,1); + Fraction aResizeY(1,1); + if (const SdrResizeXOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_RESIZEXONE)) + { + aResizeX *= pPoolItem->GetValue(); + } + if (const SdrResizeYOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_RESIZEYONE)) + { + aResizeY *= 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(m_pUserCall) + aBoundRect0 = GetLastBoundRect(); + + InternalSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, true); + SetChanged(); + BroadcastObjectChange(); + SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect0); +} + +void SdrObject::NbcSetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr) +{ + InternalSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, false); +} + +void SdrObject::InternalSetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, bool bBroadcast) +{ + GetProperties().SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast); +} + +// Broadcasting while setting attributes is managed by the AttrObj. + + +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 (m_pPlusData!=nullptr) return m_pPlusData->pGluePoints.get(); + return nullptr; +} + + +SdrGluePointList* SdrObject::ForceGluePointList() +{ + ImpForcePlusData(); + if (m_pPlusData->pGluePoints==nullptr) { + m_pPlusData->pGluePoints.reset(new SdrGluePointList); + } + return m_pPlusData->pGluePoints.get(); +} + +void SdrObject::SetGlueReallyAbsolute(bool bOn) +{ + // First a const call to see whether there are any gluepoints. + // Force const call! + if (GetGluePointList()!=nullptr) { + SdrGluePointList* pGPL=ForceGluePointList(); + pGPL->SetReallyAbsolute(bOn,*this); + } +} + +void SdrObject::NbcRotateGluePoints(const Point& rRef, Degree100 nAngle, double sn, double cs) +{ + // First a const call to see whether there are any gluepoints. + // 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 gluepoints. + // 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 gluepoints. + // 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; +} + + +static void extractLineContourFromPrimitive2DSequence( + const drawinglayer::primitive2d::Primitive2DContainer& rxSequence, + basegfx::B2DPolygonVector& rExtractedHairlines, + basegfx::B2DPolyPolygonVector& rExtractedLineFills) +{ + rExtractedHairlines.clear(); + rExtractedLineFills.clear(); + + if(rxSequence.empty()) + return; + + // 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; + drawinglayer::primitive2d::Primitive2DContainer xSequence; + GetViewContact().getViewIndependentPrimitive2DContainer(xSequence); + + if(!xSequence.empty()) + { + basegfx::B2DPolygonVector aExtractedHairlines; + basegfx::B2DPolyPolygonVector aExtractedLineFills; + + extractLineContourFromPrimitive2DSequence(xSequence, aExtractedHairlines, aExtractedLineFills); + + // for SdrObject creation, just copy all to a single Hairline-PolyPolygon + for(const 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(std::move(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(), + SdrObjKind::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(), + SdrObjKind::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) +{ + m_bMarkProt = bProt; +} + + +void SdrObject::SetEmptyPresObj(bool bEpt) +{ + m_bEmptyPresObj = bEpt; +} + + +void SdrObject::SetNotVisibleAsMaster(bool bFlg) +{ + m_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; +} + + +SdrObjectUniquePtr SdrObject::ConvertToPolyObj(bool bBezier, bool bLineToArea) const +{ + SdrObjectUniquePtr pRet = DoConvertToPolyObj(bBezier, true); + + if(pRet && bLineToArea) + { + SdrObject* pNewRet = ConvertToContourObj(pRet.get()); + pRet.reset(pNewRet); + } + + // #i73441# preserve LayerID + if(pRet && pRet->GetLayer() != GetLayer()) + { + pRet->SetLayer(GetLayer()); + } + + return pRet; +} + + +SdrObjectUniquePtr 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 != m_pPlusData && nullptr != m_pPlusData->pBroadcast) + { + SdrHint aHint(bIsInserted ? SdrHintKind::ObjectInserted : SdrHintKind::ObjectRemoved, *this); + m_pPlusData->pBroadcast->Broadcast(aHint); + } +} + +void SdrObject::SetMoveProtect(bool bProt) +{ + if(IsMoveProtect() != bProt) + { + // #i77187# secured and simplified + m_bMovProt = bProt; + SetChanged(); + BroadcastObjectChange(); + } +} + +void SdrObject::SetResizeProtect(bool bProt) +{ + if(IsResizeProtect() != bProt) + { + // #i77187# secured and simplified + m_bSizProt = bProt; + SetChanged(); + BroadcastObjectChange(); + } +} + +void SdrObject::SetPrintable(bool bPrn) +{ + if( bPrn == m_bNoPrint ) + { + m_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 (m_pPlusData==nullptr || m_pPlusData->pUserDataList==nullptr) return 0; + return m_pPlusData->pUserDataList->GetUserDataCount(); +} + +SdrObjUserData* SdrObject::GetUserData(sal_uInt16 nNum) const +{ + if (m_pPlusData==nullptr || m_pPlusData->pUserDataList==nullptr) return nullptr; + return &m_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 (!m_pPlusData->pUserDataList) + m_pPlusData->pUserDataList.reset( new SdrObjUserDataList ); + + m_pPlusData->pUserDataList->AppendUserData(std::move(pData)); +} + +void SdrObject::DeleteUserData(sal_uInt16 nNum) +{ + sal_uInt16 nCount=GetUserDataCount(); + if (nNum<nCount) { + m_pPlusData->pUserDataList->DeleteUserData(nNum); + if (nCount==1) { + m_pPlusData->pUserDataList.reset(); + } + } else { + OSL_FAIL("SdrObject::DeleteUserData(): Invalid Index."); + } +} + +void SdrObject::SetUserCall(SdrObjUserCall* pUser) +{ + m_pUserCall = pUser; +} + + +void SdrObject::SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle& rBoundRect) const +{ + SdrObject* pGroup(getParentSdrObjectFromSdrObject()); + + if ( m_pUserCall ) + { + m_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::ShapePropertyProviderId::Size ); + [[fallthrough]]; // RESIZE might also imply a change of the position + case SdrUserCallType::MoveOnly: + notifyShapePropertyChange( svx::ShapePropertyProviderId::Position ); + break; + default: + // not interested in + break; + } +} + +void SdrObject::setUnoShape( const uno::Reference< drawing::XShape >& _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 = comphelper::getFromUnoTunnel<SvxShape>( _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::drawing::XShape > SdrObject::getUnoShape() +{ + // try weak reference first + uno::Reference< css::drawing::XShape > xShape( getWeakUnoShape() ); + if( xShape ) + return xShape; + + OSL_ENSURE( mpSvxShape == nullptr, "SdrObject::getUnoShape: XShape already dead, but still an IMPL pointer!" ); + + // try to access SdrPage from this SdrObject. This will only exist if the SdrObject is + // inserted in a SdrObjList (page/group/3dScene) + SdrPage* pPageCandidate(getSdrPageFromSdrObject()); + + // tdf#12152, tdf#120728 + // + // With the paradigm change to only get a SdrPage for a SdrObject when the SdrObject + // is *inserted*, the functionality for creating 1:1 associated UNO API implementation + // SvxShapes was partially broken: The used ::CreateShape relies on the SvxPage being + // derived and the CreateShape method overloaded, implementing additional SdrInventor + // types as needed. + // + // The fallback to use SvxDrawPage::CreateShapeByTypeAndInventor is a trap: It's only + // a static fallback that handles the SdrInventor types SdrInventor::E3d and + // SdrInventor::Default. Due to that, e.g. the ReportDesigner broke in various conditions. + // + // That again has to do with the ReportDesigner being implemented using the UNO API + // aspects of SdrObjects early during their construction, not just after these are + // inserted to a SdrPage - but that is not illegal or wrong, the SdrObject exists already. + // + // As a current solution, use the (now always available) SdrModel and any of the + // existing SdrPages. The only important thing is to get a SdrPage where ::CreateShape is + // overloaded and implemented as needed. + // + // Note for the future: + // In a more ideal world there would be only one factory method for creating SdrObjects (not + // ::CreateShape and ::CreateShapeByTypeAndInventor). This also would not be placed at + // SdrPage/SvxPage at all, but at the Model where it belongs - where else would you expect + // objects for the current Model to be constructed? To have this at the Page only would make + // sense if different shapes would need to be constructed for different Pages in the same Model + // - this is never the case. + // At that Model extended functionality for that factory (or overloads and implementations) + // should be placed. But to be realistic, migrating the factories to Model now is too much + // work - maybe over time when melting SdrObject/SvxObject one day... + // + // More Note (added by noel grandin) + // Except that sd/ is being naughty and doing all kinds of magic during CreateShape that + // requires knowing which page the object is being created for. Fixing that would require + // moving a bunch of nasty logic from object creation time, to the point in time when + // it is actually added to a page. + if(nullptr == pPageCandidate) + { + // If not inserted, alternatively access a SdrPage using the SdrModel. There is + // no reason not to create and return a UNO API XShape when the SdrObject is not + // inserted - it may be in construction. Main paradigm is that it exists. + if(0 != getSdrModelFromSdrObject().GetPageCount()) + { + // Take 1st SdrPage. That may be e.g. a special page (in SD), but the + // to-be-used method ::CreateShape will be correctly overloaded in + // all cases + pPageCandidate = getSdrModelFromSdrObject().GetPage(0); + } + } + + if(nullptr != pPageCandidate) + { + uno::Reference< uno::XInterface > xPage(pPageCandidate->getUnoPage()); + if( xPage.is() ) + { + SvxDrawPage* pDrawPage = comphelper::getFromUnoTunnel<SvxDrawPage>(xPage); + if( pDrawPage ) + { + // create one + xShape = pDrawPage->CreateShape( this ); + setUnoShape( xShape ); + } + } + } + else + { + // Fallback to static base functionality. CAUTION: This will only support + // the most basic stuff like SdrInventor::E3d and SdrInventor::Default. All + // the other SdrInventor enum entries are from overloads and are *not accessible* + // using this fallback (!) - what a bad trap + rtl::Reference<SvxShape> xNewShape = SvxDrawPage::CreateShapeByTypeAndInventor( GetObjIdentifier(), GetObjInventor(), this ); + mpSvxShape = xNewShape.get(); + maWeakUnoShape = xShape = mpSvxShape; + } + + return xShape; +} + +svx::PropertyChangeNotifier& SdrObject::getShapePropertyChangeNotifier() +{ + DBG_TESTSOLARMUTEX(); + + SvxShape* pSvxShape = getSvxShape(); + ENSURE_OR_THROW( pSvxShape, "no SvxShape, yet!" ); + return pSvxShape->getShapePropertyChangeNotifier(); +} + +void SdrObject::notifyShapePropertyChange( const svx::ShapePropertyProviderId _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; +} + +void SdrObject::MakeNameUnique() +{ + if (GetName().isEmpty()) + { + if (const E3dScene* pE3dObj = dynamic_cast<const E3dScene*>(this)) + { + SdrObjList* pObjList = pE3dObj->GetSubList(); + if (pObjList) + { + SdrObject* pObj0 = pObjList->GetObj(0); + if (pObj0) + SetName(pObj0->TakeObjNameSingul()); + } + } + else + SetName(TakeObjNameSingul()); + } + + std::unordered_set<OUString> aNameSet; + MakeNameUnique(aNameSet); +} + +void SdrObject::MakeNameUnique(std::unordered_set<OUString>& rNameSet) +{ + if (GetName().isEmpty()) + return; + + if (rNameSet.empty()) + { + SdrPage* pPage; + SdrObject* pObj; + for (sal_uInt16 nPage(0); nPage < mrSdrModelFromSdrObject.GetPageCount(); ++nPage) + { + pPage = mrSdrModelFromSdrObject.GetPage(nPage); + SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups); + while (aIter.IsMore()) + { + pObj = aIter.Next(); + if (pObj != this) + rNameSet.insert(pObj->GetName()); + } + } + } + + OUString sName(GetName().trim()); + OUString sRootName(sName); + + if (!sName.isEmpty() && rtl::isAsciiDigit(sName[sName.getLength() - 1])) + { + sal_Int32 nPos(sName.getLength() - 1); + while (nPos > 0 && rtl::isAsciiDigit(sName[--nPos])); + sRootName = o3tl::trim(sName.subView(0, nPos + 1)); + } + else + sName += " 1"; + + for (sal_uInt32 n = 1; rNameSet.find(sName) != rNameSet.end(); n++) + sName = sRootName + " " + OUString::number(n); + rNameSet.insert(sName); + + SetName(sName); +} + +void SdrObject::ForceMetricToItemPoolMetric(basegfx::B2DPolyPolygon& rPolyPolygon) const noexcept +{ + MapUnit eMapUnit(getSdrModelFromSdrObject().GetItemPool().GetMetric(0)); + if(eMapUnit == MapUnit::Map100thMM) + return; + + if (const auto eTo = MapToO3tlLength(eMapUnit); eTo != o3tl::Length::invalid) + { + const double fConvert(o3tl::convert(1.0, o3tl::Length::mm100, eTo)); + rPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(fConvert, fConvert)); + } + else + { + OSL_FAIL("Missing unit translation to PoolMetric!"); + } +} + +SdrObject* SdrObjFactory::CreateObjectFromFactory(SdrModel& rSdrModel, SdrInventor nInventor, SdrObjKind 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, + SdrObjKind nIdentifier, + const tools::Rectangle* pSnapRect) +{ + SdrObject* pObj(nullptr); + bool bSetSnapRect(nullptr != pSnapRect); + + if (nInventor == SdrInventor::Default) + { + switch (nIdentifier) + { + case SdrObjKind::Measure: + { + if(nullptr != pSnapRect) + { + pObj = new SdrMeasureObj( + rSdrModel, + pSnapRect->TopLeft(), + pSnapRect->BottomRight()); + } + else + { + pObj = new SdrMeasureObj(rSdrModel); + } + } + break; + case SdrObjKind::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, + SdrObjKind::Line, + basegfx::B2DPolyPolygon(aPoly)); + } + else + { + pObj = new SdrPathObj( + rSdrModel, + SdrObjKind::Line); + } + } + break; + case SdrObjKind::Text: + case SdrObjKind::TitleText: + case SdrObjKind::OutlineText: + { + if(nullptr != pSnapRect) + { + pObj = new SdrRectObj( + rSdrModel, + nIdentifier, + *pSnapRect); + bSetSnapRect = false; + } + else + { + pObj = new SdrRectObj( + rSdrModel, + nIdentifier); + } + } + break; + case SdrObjKind::CircleOrEllipse: + case SdrObjKind::CircleSection: + case SdrObjKind::CircleArc: + case SdrObjKind::CircleCut: + { + SdrCircKind eCircKind = ToSdrCircKind(nIdentifier); + if(nullptr != pSnapRect) + { + pObj = new SdrCircObj(rSdrModel, eCircKind, *pSnapRect); + bSetSnapRect = false; + } + else + { + pObj = new SdrCircObj(rSdrModel, eCircKind); + } + } + break; + case SdrObjKind::NONE : pObj=new SdrObject(rSdrModel); break; + case SdrObjKind::Group : pObj=new SdrObjGroup(rSdrModel); break; + case SdrObjKind::Polygon : pObj=new SdrPathObj(rSdrModel, SdrObjKind::Polygon ); break; + case SdrObjKind::PolyLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PolyLine ); break; + case SdrObjKind::PathLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PathLine ); break; + case SdrObjKind::PathFill : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PathFill ); break; + case SdrObjKind::FreehandLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::FreehandLine ); break; + case SdrObjKind::FreehandFill : pObj=new SdrPathObj(rSdrModel, SdrObjKind::FreehandFill ); break; + case SdrObjKind::PathPoly : pObj=new SdrPathObj(rSdrModel, SdrObjKind::Polygon ); break; + case SdrObjKind::PathPolyLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PolyLine ); break; + case SdrObjKind::Edge : pObj=new SdrEdgeObj(rSdrModel); break; + case SdrObjKind::Rectangle : pObj=new SdrRectObj(rSdrModel); break; + case SdrObjKind::Graphic : pObj=new SdrGrafObj(rSdrModel); break; + case SdrObjKind::OLE2 : pObj=new SdrOle2Obj(rSdrModel); break; + case SdrObjKind::OLEPluginFrame : pObj=new SdrOle2Obj(rSdrModel, true); break; + case SdrObjKind::Caption : pObj=new SdrCaptionObj(rSdrModel); break; + case SdrObjKind::Page : pObj=new SdrPageObj(rSdrModel); break; + case SdrObjKind::UNO : pObj=new SdrUnoObj(rSdrModel, OUString()); break; + case SdrObjKind::CustomShape: pObj=new SdrObjCustomShape(rSdrModel); break; +#if HAVE_FEATURE_AVMEDIA + case SdrObjKind::Media : pObj=new SdrMediaObj(rSdrModel); break; +#endif + case SdrObjKind::Table : pObj=new sdr::table::SdrTableObj(rSdrModel); break; + default: 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: */ |