/* -*- 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 .
*/
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();
}
void SdrObject::setParentOfSdrObject(SdrObjList* pNewObjList)
{
assert(!pNewObjList || mpParentOfSdrObject != pNewObjList); if(mpParentOfSdrObject == pNewObjList) return; // we need to be removed from the old parent before we are attached to the new parent
assert(bool(mpParentOfSdrObject) != bool(pNewObjList) && "may only transition empty->full or full->empty");
// 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);
}
}
// 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_pGrabBagItem.reset(); if (rSource.m_pGrabBagItem!=nullptr)
m_pGrabBagItem.reset(rSource.m_pGrabBagItem->Clone());
impAddIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject());
}
SdrObject::~SdrObject()
{ #ifdef DBG_UTIL // see logic in SdrObject::release
assert(m_refCount == -1); #endif // 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);
}
void SdrObject::release() noexcept
{
oslInterlockedCount x = osl_atomic_decrement( &m_refCount ); if ( x == 0 )
{
disposeWeakConnectionPoint(); #ifdef DBG_UTIL // make sure it doesn't accidentally come back to life, see assert in acquire()
osl_atomic_decrement( &m_refCount ); #endif deletethis;
}
}
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();
}
}
}
// 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
{ autoconst& rRectangle = getOutRectangle(); if (rRectangle.IsEmpty())
{ const_cast< SdrObject* >(this)->RecalcBoundRect();
}
return rRectangle;
}
// 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 getOutRectangle();
}
void SdrObject::RecalcBoundRect()
{ // #i101680# suppress BoundRect calculations on import(s) if ((getSdrModelFromSdrObject().isLocked()) || comphelper::IsFuzzing()) return;
autoconst& rRectangle = getOutRectangle(); // central new method which will calculate the BoundRect using primitive geometry if (!rRectangle.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));
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));
// create cloned object without text, but with drawing::LineStyle_SOLID, // COL_BLACK as line color and drawing::FillStyle_NONE
rtl::Reference<SdrObject> pClone(CloneSdrObject(getSdrModelFromSdrObject()));
if(pTextObj)
{ // no text and no text animation
pClone->SetMergedItem(SdrTextAniKindItem(SdrTextAniKind::NONE));
pClone->SetOutlinerParaObject(std::nullopt);
}
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);
// #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]);
}
}
}
}
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
}
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. if (SdrObject* pTopGroupObj = getParentSdrObjectFromSdrObject())
{ while (SdrObject* pParent = pTopGroupObj->getParentSdrObjectFromSdrObject())
pTopGroupObj = pParent; // 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::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);
}
}
// 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();
}
// 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() && !comphelper::IsFuzzing())
{ // merge to a single tools::PolyPolygon (OR)
aMergedLineFillPolyPolygon = basegfx::utils::mergeToSinglePolyPolygon(std::move(aExtractedLineFills));
}
}
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,
std::move(aMergedHairlinePolyPolygon));
// 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);
// do we need a group? if(bBuildGroup || bAddOriginalGeometry)
{
rtl::Reference<SdrObject> xGroup = 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));
if(!pRetval)
{ // due to current method usage, create and return a clone when nothing has changed
pRetval = CloneSdrObject(getSdrModelFromSdrObject());
}
// convert this path object to contour object, even when it is a group
rtl::Reference<SdrObject> SdrObject::ConvertToContourObj(SdrObject* pRet1, bool bForceLineDash) const
{
rtl::Reference<SdrObject> pRet = pRet1; if(dynamic_cast<const SdrObjGroup*>( pRet.get()) != nullptr)
{
SdrObjList* pObjList2 = pRet->GetSubList();
rtl::Reference<SdrObject> pGroup = new SdrObjGroup(getSdrModelFromSdrObject());
for (const rtl::Reference<SdrObject>& pIterObj : *pObjList2)
pGroup->GetSubList()->NbcInsertObject(ConvertToContourObj(pIterObj.get(), bForceLineDash).get());
pRet = std::move(pGroup);
} else
{ if (SdrPathObj *pPathObj = dynamic_cast<SdrPathObj*>(pRet.get()))
{ // 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);
}
// notify our UNO shape listeners switch ( eUserCall )
{ case SdrUserCallType::Resize:
notifyShapePropertyChange( u"Size"_ustr );
[[fallthrough]]; // RESIZE might also imply a change of the position case SdrUserCallType::MoveOnly:
notifyShapePropertyChange( u"Position"_ustr ); 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;
}
if ( xOldUnoShape.is() )
{ // Remove yourself from the current UNO shape. Its destructor // will reset our UNO shape otherwise.
mpSvxShape->InvalidateSdrObject();
}
/** 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 ); //#113608#, make sure mpSvxShape is always synchronized with maWeakUnoShape if ( mpSvxShape && !xShape )
mpSvxShape = nullptr;
return mpSvxShape;
}
css::uno::Reference< css::drawing::XShape > SdrObject::getUnoShape()
{ // try weak reference first
uno::Reference< css::drawing::XShape > xShape = maWeakUnoShape; if (xShape) return xShape;
// 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 );
assert(xShape);
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;
}
SvxShape* pSvxShape = const_cast< SdrObject* >( this )->getSvxShape(); if ( pSvxShape ) return pSvxShape->notifyPropertyChange( rPropName );
}
// 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());
// 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(aScale.getX() < 0.0 && 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(basegfx::fround<tools::Long>(aTranslate.getX()),
basegfx::fround<tools::Long>(aTranslate.getY()));
tools::Rectangle aBaseRect(aPoint, Size(basegfx::fround<tools::Long>(aScale.getX()),
basegfx::fround<tools::Long>(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;
}
// #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::SetContextWritingMode( const sal_Int16 /*_nContextWritingMode*/ )
{ // this base class does not support different writing modes, so ignore the call
}
if (constauto eTo = MapToO3tlLength(eMapUnit); eTo != o3tl::Length::invalid)
{ constdouble fConvert(o3tl::convert(1.0, o3tl::Length::mm100, eTo));
rPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(fConvert, fConvert));
} else
{
OSL_FAIL("Missing unit translation to PoolMetric!");
}
}
SdrTextObj* DynCastSdrTextObj(SdrObject* pObj)
{ // SdrTextObj has a lot of subclasses, with lots of SdrObjKind identifiers, so use a virtual method // to be safer. if( pObj && pObj->IsSdrTextObj() ) returnstatic_cast<SdrTextObj*>(pObj); return nullptr;
}
SdrOle2Obj* DynCastSdrOle2Obj(SdrObject* pObj)
{ // SdrTextObj has subclasses, with lots of SdrObjKind identifiers, so use a virtual method // to be safer. if( pObj && pObj->IsSdrOle2Obj() ) returnstatic_cast<SdrOle2Obj*>(pObj); return nullptr;
}
// SdrObject subclass, which represents an empty object of a // certain type (kind). template <SdrObjKind OBJECT_KIND, SdrInventor OBJECT_INVENTOR> class EmptyObject final : public SdrObject
{ private: virtual ~EmptyObject() override
{}
void NbcRotate(const Point& /*rRef*/, Degree100 /*nAngle*/, double /*sinAngle*/, double /*cosAngle*/) override
{
assert(false); // should not be called for this kind of objects
}
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.