Files
odfstudio/svx/source/svdraw/svdoashp.cxx
T
Armin Le Grand (collabora) 5519a3d407 Correct StyleSheet handling when cloning to different Model II
Until now the 'local' StyleSheet got set, but that may
not be the same as the original. It was/gets identified just
by searching by name and type, not comparing content.

What to do? We could...

(a) the Style-Friendly way:
 - check if StyleSheet is identical
  - if not, create a new in-between StyleSheet hosting missing Items
  - if yes, use, done.

(b) the pragmatic/practical way:
 - Just hard-attribute the Items that are detected as different
   similar to what BurnInStyleSheetAttributes does/did, but only
   for a view differing Items

(b) is the most simple for now, but will lead to more hard
attributes. The other possibilities will be better in the
long term. For now, I will use (b).

Note that there are more cases in that VERY complex and grown
stuff emerging from SdDrawDocument::PasteBookmarkAsPage. More
things will need to be fixed, see this as a 1st step.

Needed to adapt postProcessAfterCloneToDifferentModel due to
UT testTdf96206 failing, see comment there for details.

That 1st implementation showed weaknesses with those Items
critical and only on MasterPages, so decided to give up on
that and implement ContentCompare to SdPage::Equals. That
was easier than thought: No parameter needed, only used by
InsertBookmarkAsPage_FindDuplicateLayouts::operator(). Thus,
SdPage::Equals needs no change, just SdrObject::Equals.

Due to still problems with Items corrected in Objects on
MasterPages I will for now exclude this.

Could now isolate remaining problems, these are defaults
and/or PresObj things on MasterPages. Important is that I can
completely correct all Items for all Objects in
postProcessAfterCloneToDifferentModel. I can react to these
false-positives in the ContentCompare in SdrObject::Equals.

Signed-off-by: Armin Le Grand (collabora) <Armin.LeGrand@collabora.com>
Change-Id: I10e17c8d6a893dbe92ab716182f07bafce1f3284
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/6595
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Armin Le Grand <armin.legrand@collabora.com>
(cherry picked from commit 92ffaeac04c5c8586006b026f29ce12fba3674f1)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211729
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
2026-10-05 08:52:54 +02:00

3258 lines
122 KiB
C++

/* -*- 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 <vcl/bitmap/BitmapShadowFilter.hxx>
#include <svx/svdoashp.hxx>
#include <svx/unoapi.hxx>
#include <com/sun/star/loader/CannotActivateFactoryException.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/drawing/XCustomShapeEngine.hpp>
#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/awt/Rectangle.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <com/sun/star/uno/Sequence.h>
#include <tools/helpers.hxx>
#include <svx/svddrag.hxx>
#include <svx/svddrgmt.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdpage.hxx>
#include <svx/svditer.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdtrans.hxx>
#include <svx/dialmgr.hxx>
#include <svx/strings.hrc>
#include <editeng/eeitem.hxx>
#include <editeng/editstat.hxx>
#include <editeng/adjustitem.hxx>
#include <svx/svdoutl.hxx>
#include <editeng/outlobj.hxx>
#include <svx/sdtfchim.hxx>
#include <svx/EnhancedCustomShapeGeometry.hxx>
#include <svx/EnhancedCustomShapeTypeNames.hxx>
#include <svx/EnhancedCustomShape2d.hxx>
#include <com/sun/star/beans/PropertyValues.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
#include <editeng/writingmodeitem.hxx>
#include <svx/xlineit0.hxx>
#include <svx/xlnclit.hxx>
#include <sdr/properties/customshapeproperties.hxx>
#include <sdr/contact/viewcontactofsdrobjcustomshape.hxx>
#include <svx/xlntrit.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xfltrit.hxx>
#include <svx/xflclit.hxx>
#include <svx/xflgrit.hxx>
#include <svx/xflhtit.hxx>
#include <svx/xbtmpit.hxx>
#include <vcl/virdev.hxx>
#include <svx/svdview.hxx>
#include <svx/sdmetitm.hxx>
#include <svx/sdprcitm.hxx>
#include <svx/sdshitm.hxx>
#include <svx/sdsxyitm.hxx>
#include <svx/sdtmfitm.hxx>
#include <svx/sdasitm.hxx>
#include <svx/diagram/DiagramHelper_svx.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/range/b2drange.hxx>
#include <svdobjplusdata.hxx>
#include <sal/log.hxx>
#include <tools/debug.hxx>
#include <o3tl/string_view.hxx>
#include "presetooxhandleadjustmentrelations.hxx"
#include <editeng/frmdiritem.hxx>
#include <EnhancedCustomShapeEngine.hxx>
using namespace ::com::sun::star;
static void lcl_ShapeSegmentFromBinary( drawing::EnhancedCustomShapeSegment& rSegInfo, sal_uInt16 nSDat )
{
switch( nSDat >> 8 )
{
case 0x00 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
rSegInfo.Count = nSDat & 0xff;
if ( !rSegInfo.Count )
rSegInfo.Count = 1;
break;
case 0x20 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CURVETO;
rSegInfo.Count = nSDat & 0xff;
if ( !rSegInfo.Count )
rSegInfo.Count = 1;
break;
case 0x40 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
rSegInfo.Count = nSDat & 0xff;
if ( !rSegInfo.Count )
rSegInfo.Count = 1;
break;
case 0x60 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
rSegInfo.Count = 0;
break;
case 0x80 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
rSegInfo.Count = 0;
break;
case 0xa1 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO;
rSegInfo.Count = ( nSDat & 0xff ) / 3;
break;
case 0xa2 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE;
rSegInfo.Count = ( nSDat & 0xff ) / 3;
break;
case 0xa3 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ARCTO;
rSegInfo.Count = ( nSDat & 0xff ) >> 2;
break;
case 0xa4 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ARC;
rSegInfo.Count = ( nSDat & 0xff ) >> 2;
break;
case 0xa5 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO;
rSegInfo.Count = ( nSDat & 0xff ) >> 2;
break;
case 0xa6 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC;
rSegInfo.Count = ( nSDat & 0xff ) >> 2;
break;
case 0xa7 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX;
rSegInfo.Count = nSDat & 0xff;
break;
case 0xa8 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY;
rSegInfo.Count = nSDat & 0xff;
break;
case 0xaa :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::NOFILL;
rSegInfo.Count = 0;
break;
case 0xab :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::NOSTROKE;
rSegInfo.Count = 0;
break;
default:
case 0xf8 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::UNKNOWN;
rSegInfo.Count = nSDat;
break;
}
}
static MSO_SPT ImpGetCustomShapeType( const SdrObjCustomShape& rCustoShape )
{
MSO_SPT eRetValue = mso_sptNil;
OUString aEngine( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
if ( aEngine.isEmpty() || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
{
OUString sShapeType;
const SdrCustomShapeGeometryItem& rGeometryItem( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( u"Type"_ustr );
if ( pAny && ( *pAny >>= sShapeType ) )
eRetValue = EnhancedCustomShapeTypeNames::Get( sShapeType );
}
return eRetValue;
};
static bool ImpVerticalSwitch( const SdrObjCustomShape& rCustoShape )
{
bool bRet = false;
MSO_SPT eShapeType( ImpGetCustomShapeType( rCustoShape ) );
switch( eShapeType )
{
case mso_sptAccentBorderCallout90 : // 2 ortho
case mso_sptBorderCallout1 : // 2 diag
case mso_sptBorderCallout2 : // 3
{
bRet = true;
}
break;
default: break;
}
return bRet;
}
// #i37011# create a clone with all attributes changed to shadow attributes
// and translation executed, too.
static rtl::Reference<SdrObject> ImpCreateShadowObjectClone(const SdrObject& rOriginal, const SfxItemSet& rOriginalSet)
{
rtl::Reference<SdrObject> pRetval;
const bool bShadow(rOriginalSet.Get(SDRATTR_SHADOW).GetValue());
if(bShadow)
{
// create a shadow representing object
const sal_Int32 nXDist(rOriginalSet.Get(SDRATTR_SHADOWXDIST).GetValue());
const sal_Int32 nYDist(rOriginalSet.Get(SDRATTR_SHADOWYDIST).GetValue());
const ::Color aShadowColor(rOriginalSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue());
const sal_uInt16 nShadowTransparence(rOriginalSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue());
pRetval = rOriginal.CloneSdrObject(rOriginal.getSdrModelFromSdrObject());
DBG_ASSERT(pRetval, "ImpCreateShadowObjectClone: Could not clone object (!)");
// look for used stuff
SdrObjListIter aIterator(rOriginal);
bool bLineUsed(false);
bool bAllFillUsed(false);
bool bSolidFillUsed(false);
bool bGradientFillUsed(false);
bool bHatchFillUsed(false);
bool bBitmapFillUsed(false);
while(aIterator.IsMore())
{
SdrObject* pObj = aIterator.Next();
drawing::FillStyle eFillStyle = pObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
if(!bLineUsed)
{
drawing::LineStyle eLineStyle = pObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
if(drawing::LineStyle_NONE != eLineStyle)
{
bLineUsed = true;
}
}
if(!bAllFillUsed)
{
if(!bSolidFillUsed && drawing::FillStyle_SOLID == eFillStyle)
{
bSolidFillUsed = true;
bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
}
if(!bGradientFillUsed && drawing::FillStyle_GRADIENT == eFillStyle)
{
bGradientFillUsed = true;
bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
}
if(!bHatchFillUsed && drawing::FillStyle_HATCH == eFillStyle)
{
bHatchFillUsed = true;
bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
}
if(!bBitmapFillUsed && drawing::FillStyle_BITMAP == eFillStyle)
{
bBitmapFillUsed = true;
bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
}
}
}
// translate to shadow coordinates
pRetval->NbcMove(Size(nXDist, nYDist));
// set items as needed
SfxItemSet aTempSet(rOriginalSet);
// if a SvxWritingModeItem (Top->Bottom) is set the text object
// is creating a paraobject, but paraobjects can not be created without model. So
// we are preventing the crash by setting the writing mode always left to right,
// this is not bad since our shadow geometry does not contain text.
aTempSet.Put(SvxWritingModeItem(text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION));
// no shadow
aTempSet.Put(makeSdrShadowItem(false));
aTempSet.Put(makeSdrShadowXDistItem(0));
aTempSet.Put(makeSdrShadowYDistItem(0));
// line color and transparency like shadow
if(bLineUsed)
{
aTempSet.Put(XLineColorItem(OUString(), aShadowColor));
aTempSet.Put(XLineTransparenceItem(nShadowTransparence));
}
// fill color and transparency like shadow
if(bSolidFillUsed)
{
aTempSet.Put(XFillColorItem(OUString(), aShadowColor));
aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
}
// gradient and transparency like shadow
if(bGradientFillUsed)
{
basegfx::BGradient aGradient(rOriginalSet.Get(XATTR_FILLGRADIENT).GetGradientValue());
sal_uInt8 nStartLuminance(Color(aGradient.GetColorStops().front().getStopColor()).GetLuminance());
sal_uInt8 nEndLuminance(Color(aGradient.GetColorStops().back().getStopColor()).GetLuminance());
if(aGradient.GetStartIntens() != 100)
{
nStartLuminance = static_cast<sal_uInt8>(nStartLuminance * (static_cast<double>(aGradient.GetStartIntens()) / 100.0));
}
if(aGradient.GetEndIntens() != 100)
{
nEndLuminance = static_cast<sal_uInt8>(nEndLuminance * (static_cast<double>(aGradient.GetEndIntens()) / 100.0));
}
::Color aStartColor(
static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetRed()) / 256),
static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetGreen()) / 256),
static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetBlue()) / 256));
::Color aEndColor(
static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetRed()) / 256),
static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetGreen()) / 256),
static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetBlue()) / 256));
aGradient.SetColorStops(
basegfx::BColorStops(
aStartColor.getBColor(),
aEndColor.getBColor()));
aTempSet.Put(XFillGradientItem(aGradient));
aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
}
// hatch and transparency like shadow
if(bHatchFillUsed)
{
XHatch aHatch(rOriginalSet.Get(XATTR_FILLHATCH).GetHatchValue());
aHatch.SetColor(aShadowColor);
aTempSet.Put(XFillHatchItem(aHatch));
aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
}
// bitmap and transparency like shadow
if(bBitmapFillUsed)
{
GraphicObject aGraphicObject(rOriginalSet.Get(XATTR_FILLBITMAP).GetGraphicObject());
Bitmap aBitmap(aGraphicObject.GetGraphic().GetBitmap());
if(!aBitmap.IsEmpty())
{
ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
pVirDev->SetOutputSizePixel(aBitmap.GetSizePixel());
BitmapFilter::Filter(aBitmap, BitmapShadowFilter(aShadowColor));
pVirDev->DrawBitmap(Point(), aBitmap);
aGraphicObject.SetGraphic(Graphic(pVirDev->GetBitmap(Point(0,0), aBitmap.GetSizePixel())));
}
aTempSet.Put(XFillBitmapItem(aGraphicObject));
aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
}
// set attributes and paint shadow object
pRetval->SetMergedItemSet( aTempSet );
}
return pRetval;
}
rtl::Reference<EnhancedCustomShapeEngine> const & SdrObjCustomShape::GetCustomShapeEngine() const
{
if (mxCustomShapeEngine.is())
return mxCustomShapeEngine;
OUString aEngine(GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue());
if ( aEngine.isEmpty() || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine")
{
mxCustomShapeEngine = new EnhancedCustomShapeEngine(*const_cast<SdrObjCustomShape*>(this));
}
return mxCustomShapeEngine;
}
const SdrObject* SdrObjCustomShape::GetSdrObjectFromCustomShape() const
{
if ( !mXRenderedCustomShape.is() )
{
rtl::Reference<EnhancedCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
if ( xCustomShapeEngine.is() )
mXRenderedCustomShape = xCustomShapeEngine->render2();
}
return mXRenderedCustomShape.get();
}
// #i37011# Shadow geometry creation
const SdrObject* SdrObjCustomShape::GetSdrObjectShadowFromCustomShape() const
{
if(!mpLastShadowGeometry)
{
const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
if(pSdrObject)
{
const SfxItemSet& rOriginalSet = GetObjectItemSet();
const bool bShadow(rOriginalSet.Get( SDRATTR_SHADOW ).GetValue());
if(bShadow)
{
// create a clone with all attributes changed to shadow attributes
// and translation executed, too.
const_cast<SdrObjCustomShape*>(this)->mpLastShadowGeometry =
ImpCreateShadowObjectClone(*pSdrObject, rOriginalSet);
}
}
}
return mpLastShadowGeometry.get();
}
bool SdrObjCustomShape::IsTextPath() const
{
static constexpr OUString sTextPath( u"TextPath"_ustr );
bool bTextPathOn = false;
const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sTextPath, sTextPath );
if ( pAny )
*pAny >>= bTextPathOn;
return bTextPathOn;
}
bool SdrObjCustomShape::UseNoFillStyle() const
{
bool bRet = false;
OUString sShapeType;
static constexpr OUString sType( u"Type"_ustr );
const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sType );
if ( pAny )
*pAny >>= sShapeType;
bRet = !IsCustomShapeFilledByDefault( EnhancedCustomShapeTypeNames::Get( sType ) );
return bRet;
}
bool SdrObjCustomShape::IsMirroredX() const
{
bool bMirroredX = false;
const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( u"MirroredX"_ustr );
if ( pAny )
*pAny >>= bMirroredX;
return bMirroredX;
}
bool SdrObjCustomShape::IsMirroredY() const
{
bool bMirroredY = false;
const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( u"MirroredY"_ustr );
if ( pAny )
*pAny >>= bMirroredY;
return bMirroredY;
}
void SdrObjCustomShape::SetMirroredX( const bool bMirrorX )
{
SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
beans::PropertyValue aPropVal;
aPropVal.Name = "MirroredX";
aPropVal.Value <<= bMirrorX;
aGeometryItem.SetPropertyValue( aPropVal );
SetMergedItem( aGeometryItem );
}
void SdrObjCustomShape::SetMirroredY( const bool bMirrorY )
{
SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
beans::PropertyValue aPropVal;
aPropVal.Name = "MirroredY";
aPropVal.Value <<= bMirrorY;
aGeometryItem.SetPropertyValue( aPropVal );
SetMergedItem( aGeometryItem );
}
double SdrObjCustomShape::GetExtraTextRotation( const bool bPreRotation ) const
{
double fExtraTextRotateAngle = 0.0;
if (bPreRotation)
{
// textPreRotateAngle might be set by macro or diagram (SmartArt) import
const uno::Any* pAny;
const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
pAny = rGeometryItem.GetPropertyValueByName(u"TextPreRotateAngle"_ustr);
if ( pAny )
*pAny >>= fExtraTextRotateAngle;
// As long as the edit engine is not able to render these text directions we
// emulate them by setting a suitable text pre-rotation.
const SvxFrameDirectionItem& rDirectionItem = GetMergedItem(SDRATTR_WRITINGMODE2);
if (rDirectionItem.GetValue() == SvxFrameDirection::Vertical_RL_TB90)
fExtraTextRotateAngle -= 90;
else if (rDirectionItem.GetValue() == SvxFrameDirection::Vertical_LR_BT)
fExtraTextRotateAngle -=270;
}
else
{
const uno::Any* pAny;
const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
pAny = rGeometryItem.GetPropertyValueByName(u"TextRotateAngle"_ustr);
if ( pAny )
*pAny >>= fExtraTextRotateAngle;
}
return fExtraTextRotateAngle;
}
bool SdrObjCustomShape::GetTextBounds( tools::Rectangle& rTextBound ) const
{
bool bRet = false;
rtl::Reference<EnhancedCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
if ( xCustomShapeEngine.is() )
{
tools::Rectangle aR( xCustomShapeEngine->getTextBounds() );
if ( aR.GetWidth() > 1 && aR.GetHeight() > 1 )
{
rTextBound = aR;
bRet = true;
}
}
return bRet;
}
basegfx::B2DPolyPolygon SdrObjCustomShape::GetLineGeometry( const bool bBezierAllowed ) const
{
basegfx::B2DPolyPolygon aRetval;
rtl::Reference<EnhancedCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
if ( xCustomShapeEngine.is() )
{
aRetval = xCustomShapeEngine->getB2DLineGeometry();
try
{
if ( !bBezierAllowed && aRetval.areControlPointsUsed())
{
aRetval = basegfx::utils::adaptiveSubdivideByAngle(aRetval);
}
}
catch ( const lang::IllegalArgumentException & )
{
}
}
return aRetval;
}
std::vector< SdrCustomShapeInteraction > SdrObjCustomShape::GetInteractionHandles() const
{
std::vector< SdrCustomShapeInteraction > aRet;
try
{
rtl::Reference<EnhancedCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
if ( !xCustomShapeEngine.is() )
return aRet;
std::vector<uno::Reference<drawing::XCustomShapeHandle>> xInteractionHandles( xCustomShapeEngine->getInteraction() );
for ( size_t i = 0; i < xInteractionHandles.size(); i++ )
{
SdrCustomShapeInteraction aSdrCustomShapeInteraction;
aSdrCustomShapeInteraction.xInteraction = xInteractionHandles[ i ];
aSdrCustomShapeInteraction.aPosition = xInteractionHandles[ i ]->getPosition();
CustomShapeHandleModes nMode = CustomShapeHandleModes::NONE;
switch( ImpGetCustomShapeType( *this ) )
{
case mso_sptAccentBorderCallout90 : // 2 ortho
{
if (i == 0)
nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
else if (i == 1)
nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE | CustomShapeHandleModes::ORTHO4;
}
break;
case mso_sptChevron :
case mso_sptHomePlate :
nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX;
break;
case mso_sptWedgeRectCallout :
case mso_sptWedgeRRectCallout :
case mso_sptCloudCallout :
case mso_sptWedgeEllipseCallout :
{
if (i == 0)
nMode |= CustomShapeHandleModes::RESIZE_FIXED;
}
break;
case mso_sptBorderCallout1 : // 2 diag
{
if (i == 0)
nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
else if (i == 1)
nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
}
break;
case mso_sptBorderCallout2 : // 3
{
if (i == 0)
nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
else if (i == 2)
nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
}
break;
case mso_sptCallout90 :
case mso_sptAccentCallout90 :
case mso_sptBorderCallout90 :
case mso_sptCallout1 :
case mso_sptCallout2 :
case mso_sptCallout3 :
case mso_sptAccentCallout1 :
case mso_sptAccentCallout2 :
case mso_sptAccentCallout3 :
case mso_sptBorderCallout3 :
case mso_sptAccentBorderCallout1 :
case mso_sptAccentBorderCallout2 :
case mso_sptAccentBorderCallout3 :
{
if (i == 0)
nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
}
break;
default: break;
}
aSdrCustomShapeInteraction.nMode = nMode;
aRet.push_back( aSdrCustomShapeInteraction );
}
}
catch( const uno::RuntimeException& )
{
}
return aRet;
}
// BaseProperties section
#define DEFAULT_MINIMUM_SIGNED_COMPARE (sal_Int32(0x80000000))
#define DEFAULT_MAXIMUM_SIGNED_COMPARE (sal_Int32(0x7fffffff))
static sal_Int32 GetNumberOfProperties ( const SvxMSDffHandle* pData )
{
sal_Int32 nPropertiesNeeded=1; // position is always needed
SvxMSDffHandleFlags nFlags = pData->nFlags;
if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
nPropertiesNeeded++;
if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
nPropertiesNeeded++;
if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
nPropertiesNeeded++;
if ( nFlags & SvxMSDffHandleFlags::POLAR )
{
nPropertiesNeeded++;
if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
{
if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
}
}
else if ( nFlags & SvxMSDffHandleFlags::RANGE )
{
if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
}
return nPropertiesNeeded;
}
static void lcl_ShapePropertiesFromDFF( const SvxMSDffHandle* pData, beans::PropertyValues& rPropValues )
{
SvxMSDffHandleFlags nFlags = pData->nFlags;
sal_Int32 n=0;
auto pPropValues = rPropValues.getArray();
// POSITION
{
drawing::EnhancedCustomShapeParameterPair aPosition;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.First, pData->nPositionX, true, true );
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.Second, pData->nPositionY, true, false );
pPropValues[ n ].Name = "Position";
pPropValues[ n++ ].Value <<= aPosition;
}
if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
{
pPropValues[ n ].Name = "MirroredX";
pPropValues[ n++ ].Value <<= true;
}
if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
{
pPropValues[ n ].Name = "MirroredY";
pPropValues[ n++ ].Value <<= true;
}
if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
{
pPropValues[ n ].Name = "Switched";
pPropValues[ n++ ].Value <<= true;
}
if ( nFlags & SvxMSDffHandleFlags::POLAR )
{
drawing::EnhancedCustomShapeParameterPair aCenter;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.First, pData->nCenterX,
bool( nFlags & SvxMSDffHandleFlags::CENTER_X_IS_SPECIAL ), true );
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.Second, pData->nCenterY,
bool( nFlags & SvxMSDffHandleFlags::CENTER_Y_IS_SPECIAL ), false );
pPropValues[ n ].Name = "Polar";
pPropValues[ n++ ].Value <<= aCenter;
if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
{
if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMinimum, pData->nRangeXMin,
bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
pPropValues[ n ].Name = "RadiusRangeMinimum";
pPropValues[ n++ ].Value <<= aRadiusRangeMinimum;
}
if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMaximum, pData->nRangeXMax,
bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
pPropValues[ n ].Name = "RadiusRangeMaximum";
pPropValues[ n++ ].Value <<= aRadiusRangeMaximum;
}
}
}
else if ( nFlags & SvxMSDffHandleFlags::RANGE )
{
if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRangeXMinimum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMinimum, pData->nRangeXMin,
bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
pPropValues[ n ].Name = "RangeXMinimum";
pPropValues[ n++ ].Value <<= aRangeXMinimum;
}
if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRangeXMaximum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMaximum, pData->nRangeXMax,
bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
pPropValues[ n ].Name = "RangeXMaximum";
pPropValues[ n++ ].Value <<= aRangeXMaximum;
}
if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRangeYMinimum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMinimum, pData->nRangeYMin,
bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL ), true );
pPropValues[ n ].Name = "RangeYMinimum";
pPropValues[ n++ ].Value <<= aRangeYMinimum;
}
if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRangeYMaximum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMaximum, pData->nRangeYMax,
bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL ), false );
pPropValues[ n ].Name = "RangeYMaximum";
pPropValues[ n++ ].Value <<= aRangeYMaximum;
}
}
}
std::unique_ptr<sdr::properties::BaseProperties> SdrObjCustomShape::CreateObjectSpecificProperties()
{
return std::make_unique<sdr::properties::CustomShapeProperties>(*this);
}
SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel)
: SdrTextObj(rSdrModel)
, m_fObjectRotation(0.0)
, mbAdjustingTextFrameWidthAndHeight(false)
{
m_bClosedObj = true; // custom shapes may be filled
mbTextFrame = true;
}
SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel, SdrObjCustomShape const & rSource)
: SdrTextObj(rSdrModel, rSource)
, m_fObjectRotation(0.0)
, mbAdjustingTextFrameWidthAndHeight(false)
{
m_bClosedObj = true; // custom shapes may be filled
mbTextFrame = true;
m_fObjectRotation = rSource.m_fObjectRotation;
mbAdjustingTextFrameWidthAndHeight = rSource.mbAdjustingTextFrameWidthAndHeight;
assert(!mbAdjustingTextFrameWidthAndHeight);
InvalidateRenderGeometry();
}
SdrObjCustomShape::~SdrObjCustomShape()
{
// delete buffered display geometry
InvalidateRenderGeometry();
}
void SdrObjCustomShape::MergeDefaultAttributes( const OUString* pType )
{
beans::PropertyValue aPropVal;
OUString sShapeType;
static constexpr OUString sType( u"Type"_ustr );
SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
if ( pType && !pType->isEmpty() )
{
sal_Int32 nType = pType->toInt32();
if ( nType )
sShapeType = EnhancedCustomShapeTypeNames::Get( static_cast< MSO_SPT >( nType ) );
else
sShapeType = *pType;
aPropVal.Name = sType;
aPropVal.Value <<= sShapeType;
aGeometryItem.SetPropertyValue( aPropVal );
}
else
{
uno::Any *pAny = aGeometryItem.GetPropertyValueByName( sType );
if ( pAny )
*pAny >>= sShapeType;
}
MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
std::span<const sal_Int32> pDefData;
const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
if ( pDefCustomShape )
pDefData = pDefCustomShape->pDefData;
uno::Sequence<drawing::EnhancedCustomShapeAdjustmentValue> seqAdjustmentValues;
// AdjustmentValues
static constexpr OUString sAdjustmentValues( u"AdjustmentValues"_ustr );
const uno::Any* pAny = aGeometryItem.GetPropertyValueByName( sAdjustmentValues );
if ( pAny )
*pAny >>= seqAdjustmentValues;
if ( pDefCustomShape && !pDefData.empty() ) // now check if we have to default some adjustment values
{
// first check if there are adjustment values are to be appended
sal_Int32 i, nAdjustmentValues = seqAdjustmentValues.getLength();
sal_Int32 nAdjustmentDefaults = pDefData[0];
pDefData = pDefData.subspan(1);
if ( nAdjustmentDefaults > nAdjustmentValues )
seqAdjustmentValues.realloc( nAdjustmentDefaults );
auto pseqAdjustmentValues = seqAdjustmentValues.getArray();
for ( i = nAdjustmentValues; i < nAdjustmentDefaults; i++ )
{
pseqAdjustmentValues[ i ].Value <<= pDefData[ i ];
pseqAdjustmentValues[ i ].State = beans::PropertyState_DIRECT_VALUE;
}
// check if there are defaulted adjustment values that should be filled the hard coded defaults (pDefValue)
sal_Int32 nCount = std::min(nAdjustmentValues, nAdjustmentDefaults);
for ( i = 0; i < nCount; i++ )
{
if ( seqAdjustmentValues[ i ].State != beans::PropertyState_DIRECT_VALUE )
{
pseqAdjustmentValues[ i ].Value <<= pDefData[ i ];
pseqAdjustmentValues[ i ].State = beans::PropertyState_DIRECT_VALUE;
}
}
}
aPropVal.Name = sAdjustmentValues;
aPropVal.Value <<= seqAdjustmentValues;
aGeometryItem.SetPropertyValue( aPropVal );
// Coordsize
static constexpr OUString sViewBox( u"ViewBox"_ustr );
const uno::Any* pViewBox = aGeometryItem.GetPropertyValueByName( sViewBox );
awt::Rectangle aViewBox;
if ( !pViewBox || !(*pViewBox >>= aViewBox ) )
{
if ( pDefCustomShape )
{
aViewBox.X = 0;
aViewBox.Y = 0;
aViewBox.Width = pDefCustomShape->nCoordWidth;
aViewBox.Height= pDefCustomShape->nCoordHeight;
aPropVal.Name = sViewBox;
aPropVal.Value <<= aViewBox;
aGeometryItem.SetPropertyValue( aPropVal );
}
}
static constexpr OUString sPath( u"Path"_ustr );
// Path/Coordinates
static constexpr OUString sCoordinates( u"Coordinates"_ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sCoordinates );
if (!pAny && pDefCustomShape && !pDefCustomShape->pVertices.empty())
{
sal_Int32 i, nCount = pDefCustomShape->pVertices.size();
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqCoordinates( nCount );
auto pseqCoordinates = seqCoordinates.getArray();
for ( i = 0; i < nCount; i++ )
{
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
}
aPropVal.Name = sCoordinates;
aPropVal.Value <<= seqCoordinates;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
// Path/GluePoints
static constexpr OUString sGluePoints( u"GluePoints"_ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sGluePoints );
if (!pAny && pDefCustomShape && !pDefCustomShape->pGluePoints.empty())
{
sal_Int32 i, nCount = pDefCustomShape->pGluePoints.size();
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqGluePoints( nCount );
auto pseqGluePoints = seqGluePoints.getArray();
for ( i = 0; i < nCount; i++ )
{
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
}
aPropVal.Name = sGluePoints;
aPropVal.Value <<= seqGluePoints;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
// Path/Segments
static constexpr OUString sSegments( u"Segments"_ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sSegments );
if ( !pAny && pDefCustomShape && !pDefCustomShape->pElements.empty() )
{
sal_Int32 i, nCount = pDefCustomShape->pElements.size();
uno::Sequence<drawing::EnhancedCustomShapeSegment> seqSegments( nCount );
auto pseqSegments = seqSegments.getArray();
for ( i = 0; i < nCount; i++ )
{
drawing::EnhancedCustomShapeSegment& rSegInfo = pseqSegments[ i ];
sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
}
aPropVal.Name = sSegments;
aPropVal.Value <<= seqSegments;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
// Path/StretchX
static constexpr OUString sStretchX( u"StretchX"_ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchX );
if ( !pAny && pDefCustomShape )
{
sal_Int32 nXRef = pDefCustomShape->nXRef;
if ( nXRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
{
aPropVal.Name = sStretchX;
aPropVal.Value <<= nXRef;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
}
// Path/StretchY
static constexpr OUString sStretchY( u"StretchY"_ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchY );
if ( !pAny && pDefCustomShape )
{
sal_Int32 nYRef = pDefCustomShape->nYRef;
if ( nYRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
{
aPropVal.Name = sStretchY;
aPropVal.Value <<= nYRef;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
}
// Path/TextFrames
static constexpr OUString sTextFrames( u"TextFrames"_ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sTextFrames );
if (!pAny && pDefCustomShape && !pDefCustomShape->pTextRect.empty())
{
sal_Int32 i, nCount = pDefCustomShape->pTextRect.size();
uno::Sequence<drawing::EnhancedCustomShapeTextFrame> seqTextFrames( nCount );
auto pseqTextFrames = seqTextFrames.getArray();
for (i = 0; i < nCount; i++)
{
const SvxMSDffTextRectangles* pRectangles = &pDefCustomShape->pTextRect[i];
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].TopLeft.First, pRectangles->nPairA.nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].BottomRight.First, pRectangles->nPairB.nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
}
aPropVal.Name = sTextFrames;
aPropVal.Value <<= seqTextFrames;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
// Equations
static constexpr OUString sEquations( u"Equations"_ustr );
pAny = aGeometryItem.GetPropertyValueByName( sEquations );
if (!pAny && pDefCustomShape && !pDefCustomShape->pCalculation.empty() )
{
sal_Int32 i, nCount = pDefCustomShape->pCalculation.size();
uno::Sequence< OUString > seqEquations( nCount );
auto pseqEquations = seqEquations.getArray();
for (i = 0; i < nCount; i++)
{
const SvxMSDffCalculationData* pData = &pDefCustomShape->pCalculation[i];
pseqEquations[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
}
aPropVal.Name = sEquations;
aPropVal.Value <<= seqEquations;
aGeometryItem.SetPropertyValue( aPropVal );
}
// Handles
static constexpr OUString sHandles( u"Handles"_ustr );
pAny = aGeometryItem.GetPropertyValueByName( sHandles );
if (!pAny && pDefCustomShape && !pDefCustomShape->pHandles.empty())
{
sal_Int32 i, nCount = pDefCustomShape->pHandles.size();
uno::Sequence<beans::PropertyValues> seqHandles( nCount );
auto pseqHandles = seqHandles.getArray();
for (i = 0; i < nCount; i++)
{
const SvxMSDffHandle* pData = &pDefCustomShape->pHandles[i];
sal_Int32 nPropertiesNeeded;
beans::PropertyValues& rPropValues = pseqHandles[ i ];
nPropertiesNeeded = GetNumberOfProperties( pData );
rPropValues.realloc( nPropertiesNeeded );
lcl_ShapePropertiesFromDFF( pData, rPropValues );
}
aPropVal.Name = sHandles;
aPropVal.Value <<= seqHandles;
aGeometryItem.SetPropertyValue( aPropVal );
}
else if (pAny && sShapeType.startsWith("ooxml-") && sShapeType != "ooxml-non-primitive")
{
// ODF is not able to store the ooxml way of connecting handle to an adjustment
// value by name, e.g. attribute RefX="adj". So the information is lost, when exporting
// a pptx to odp, for example. This part reconstructs this information for the
// ooxml preset shapes from their definition.
uno::Sequence<beans::PropertyValues> seqHandles;
*pAny >>= seqHandles;
auto seqHandlesRange = asNonConstRange(seqHandles);
bool bChanged(false);
for (sal_Int32 i = 0; i < seqHandles.getLength(); i++)
{
comphelper::SequenceAsHashMap aHandleProps(seqHandles[i]);
OUString sFirstRefType;
sal_Int32 nFirstAdjRef;
OUString sSecondRefType;
sal_Int32 nSecondAdjRef;
PresetOOXHandleAdj::GetOOXHandleAdjRelation(sShapeType, i, sFirstRefType, nFirstAdjRef,
sSecondRefType, nSecondAdjRef);
if (sFirstRefType != "na" && 0 <= nFirstAdjRef
&& nFirstAdjRef < seqAdjustmentValues.getLength())
{
bChanged |= aHandleProps.createItemIfMissing(sFirstRefType, nFirstAdjRef);
}
if (sSecondRefType != "na" && 0 <= nSecondAdjRef
&& nSecondAdjRef < seqAdjustmentValues.getLength())
{
bChanged |= aHandleProps.createItemIfMissing(sSecondRefType, nSecondAdjRef);
}
aHandleProps >> seqHandlesRange[i];
}
if (bChanged)
{
aPropVal.Name = sHandles;
aPropVal.Value <<= seqHandles;
aGeometryItem.SetPropertyValue(aPropVal);
}
}
SetMergedItem( aGeometryItem );
}
bool SdrObjCustomShape::IsDefaultGeometry( const DefaultType eDefaultType ) const
{
bool bIsDefaultGeometry = false;
OUString sShapeType;
const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any *pAny = rGeometryItem.GetPropertyValueByName( u"Type"_ustr );
if ( pAny )
*pAny >>= sShapeType;
MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
static constexpr OUString sPath( u"Path"_ustr );
switch( eDefaultType )
{
case DefaultType::Viewbox :
{
const uno::Any* pViewBox = rGeometryItem.GetPropertyValueByName( u"ViewBox"_ustr );
awt::Rectangle aViewBox;
if (pViewBox && (*pViewBox >>= aViewBox) && pDefCustomShape)
{
if ( ( aViewBox.Width == pDefCustomShape->nCoordWidth )
&& ( aViewBox.Height == pDefCustomShape->nCoordHeight ) )
bIsDefaultGeometry = true;
}
}
break;
case DefaultType::Path :
{
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"Coordinates"_ustr );
if (pAny && pDefCustomShape && !pDefCustomShape->pVertices.empty())
{
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqCoordinates1;
if ( *pAny >>= seqCoordinates1 )
{
sal_Int32 i, nCount = pDefCustomShape->pVertices.size();
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqCoordinates2( nCount );
auto pseqCoordinates2 = seqCoordinates2.getArray();
for ( i = 0; i < nCount; i++ )
{
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates2[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates2[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
}
if ( seqCoordinates1 == seqCoordinates2 )
bIsDefaultGeometry = true;
}
}
else if (pDefCustomShape && pDefCustomShape->pVertices.empty())
bIsDefaultGeometry = true;
}
break;
case DefaultType::Gluepoints :
{
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"GluePoints"_ustr );
if (pAny && pDefCustomShape && !pDefCustomShape->pGluePoints.empty())
{
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqGluePoints1;
if ( *pAny >>= seqGluePoints1 )
{
sal_Int32 i, nCount = pDefCustomShape->pGluePoints.size();
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqGluePoints2( nCount );
auto pseqGluePoints2 = seqGluePoints2.getArray();
for ( i = 0; i < nCount; i++ )
{
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints2[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints2[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
}
if ( seqGluePoints1 == seqGluePoints2 )
bIsDefaultGeometry = true;
}
}
else if (pDefCustomShape && pDefCustomShape->pGluePoints.empty())
bIsDefaultGeometry = true;
}
break;
case DefaultType::Segments :
{
// Path/Segments
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"Segments"_ustr );
if ( pAny )
{
uno::Sequence<drawing::EnhancedCustomShapeSegment> seqSegments1;
if ( *pAny >>= seqSegments1 )
{
if ( pDefCustomShape && !pDefCustomShape->pElements.empty())
{
sal_Int32 i, nCount = pDefCustomShape->pElements.size();
if ( nCount )
{
uno::Sequence<drawing::EnhancedCustomShapeSegment> seqSegments2( nCount );
auto pseqSegments2 = seqSegments2.getArray();
for ( i = 0; i < nCount; i++ )
{
drawing::EnhancedCustomShapeSegment& rSegInfo = pseqSegments2[ i ];
sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
}
if ( seqSegments1 == seqSegments2 )
bIsDefaultGeometry = true;
}
}
else
{
// check if it's the default segment description ( M L Z N )
if ( seqSegments1.getLength() == 4 )
{
if ( ( seqSegments1[ 0 ].Command == drawing::EnhancedCustomShapeSegmentCommand::MOVETO )
&& ( seqSegments1[ 1 ].Command == drawing::EnhancedCustomShapeSegmentCommand::LINETO )
&& ( seqSegments1[ 2 ].Command == drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
&& ( seqSegments1[ 3 ].Command == drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH ) )
bIsDefaultGeometry = true;
}
}
}
}
else if ( pDefCustomShape && pDefCustomShape->pElements.empty() )
bIsDefaultGeometry = true;
}
break;
case DefaultType::StretchX :
{
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"StretchX"_ustr );
if ( pAny && pDefCustomShape )
{
sal_Int32 nStretchX = 0;
if ( *pAny >>= nStretchX )
{
if ( pDefCustomShape->nXRef == nStretchX )
bIsDefaultGeometry = true;
}
}
else if ( pDefCustomShape && ( pDefCustomShape->nXRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
bIsDefaultGeometry = true;
}
break;
case DefaultType::StretchY :
{
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"StretchY"_ustr );
if ( pAny && pDefCustomShape )
{
sal_Int32 nStretchY = 0;
if ( *pAny >>= nStretchY )
{
if ( pDefCustomShape->nYRef == nStretchY )
bIsDefaultGeometry = true;
}
}
else if ( pDefCustomShape && ( pDefCustomShape->nYRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
bIsDefaultGeometry = true;
}
break;
case DefaultType::Equations :
{
pAny = rGeometryItem.GetPropertyValueByName( u"Equations"_ustr );
if (pAny && pDefCustomShape && !pDefCustomShape->pCalculation.empty())
{
uno::Sequence<OUString> seqEquations1;
if ( *pAny >>= seqEquations1 )
{
sal_Int32 i, nCount = pDefCustomShape->pCalculation.size();
uno::Sequence<OUString> seqEquations2( nCount );
auto pseqEquations2 = seqEquations2.getArray();
for (i = 0; i < nCount; i++)
{
const SvxMSDffCalculationData* pData = &pDefCustomShape->pCalculation[i];
pseqEquations2[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
}
if ( seqEquations1 == seqEquations2 )
bIsDefaultGeometry = true;
}
}
else if (pDefCustomShape && pDefCustomShape->pCalculation.empty())
bIsDefaultGeometry = true;
}
break;
case DefaultType::TextFrames :
{
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"TextFrames"_ustr );
if (pAny && pDefCustomShape && !pDefCustomShape->pTextRect.empty())
{
uno::Sequence<drawing::EnhancedCustomShapeTextFrame> seqTextFrames1;
if ( *pAny >>= seqTextFrames1 )
{
sal_Int32 i, nCount = pDefCustomShape->pTextRect.size();
uno::Sequence<drawing::EnhancedCustomShapeTextFrame> seqTextFrames2( nCount );
auto pseqTextFrames2 = seqTextFrames2.getArray();
for (i = 0; i < nCount; i++)
{
const SvxMSDffTextRectangles* pRectangles = &pDefCustomShape->pTextRect[i];
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].TopLeft.First, pRectangles->nPairA.nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].BottomRight.First, pRectangles->nPairB.nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
}
if ( seqTextFrames1 == seqTextFrames2 )
bIsDefaultGeometry = true;
}
}
else if (pDefCustomShape && pDefCustomShape->pTextRect.empty())
bIsDefaultGeometry = true;
}
break;
}
return bIsDefaultGeometry;
}
void SdrObjCustomShape::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
{
rInfo.bResizeFreeAllowed=m_fObjectRotation == 0.0;
rInfo.bResizePropAllowed=true;
rInfo.bRotateFreeAllowed=true;
rInfo.bRotate90Allowed =true;
rInfo.bMirrorFreeAllowed=true;
rInfo.bMirror45Allowed =true;
rInfo.bMirror90Allowed =true;
rInfo.bTransparenceAllowed = false;
rInfo.bShearAllowed =true;
rInfo.bEdgeRadiusAllowed=false;
rInfo.bNoContortion =true;
// #i37011#
if ( !mXRenderedCustomShape.is() )
return;
// #i37262#
// Iterate self over the contained objects, since there are combinations of
// polygon and curve objects. In that case, aInfo.bCanConvToPath and
// aInfo.bCanConvToPoly would be false. What is needed here is an or, not an and.
SdrObjListIter aIterator(*mXRenderedCustomShape);
while(aIterator.IsMore())
{
SdrObject* pCandidate = aIterator.Next();
SdrObjTransformInfoRec aInfo;
pCandidate->TakeObjInfo(aInfo);
// set path and poly conversion if one is possible since
// this object will first be broken
const bool bCanConvToPathOrPoly(aInfo.bCanConvToPath || aInfo.bCanConvToPoly);
if(rInfo.bCanConvToPath != bCanConvToPathOrPoly)
{
rInfo.bCanConvToPath = bCanConvToPathOrPoly;
}
if(rInfo.bCanConvToPoly != bCanConvToPathOrPoly)
{
rInfo.bCanConvToPoly = bCanConvToPathOrPoly;
}
if(rInfo.bCanConvToContour != aInfo.bCanConvToContour)
{
rInfo.bCanConvToContour = aInfo.bCanConvToContour;
}
if(rInfo.bShearAllowed != aInfo.bShearAllowed)
{
rInfo.bShearAllowed = aInfo.bShearAllowed;
}
}
}
SdrObjKind SdrObjCustomShape::GetObjIdentifier() const
{
return SdrObjKind::CustomShape;
}
// #115391# This implementation is based on the TextFrame size of the CustomShape and the
// state of the ResizeShapeToFitText flag to correctly set TextMinFrameWidth/Height
void SdrObjCustomShape::AdaptTextMinSize()
{
if (getSdrModelFromSdrObject().IsCreatingDataObj() || getSdrModelFromSdrObject().IsPasteResize())
return;
// check if we need to change anything before creating an SfxItemSet, because that is expensive
const bool bResizeShapeToFitText(GetObjectItem(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue());
tools::Rectangle aTextBound(getRectangle());
bool bChanged(false);
if(bResizeShapeToFitText)
bChanged = true;
else if(GetTextBounds(aTextBound))
bChanged = true;
if (!bChanged)
return;
SfxItemSet aSet(SfxItemSet::makeFixedSfxItemSet<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH> // contains SDRATTR_TEXT_MAXFRAMEWIDTH
(*GetObjectItemSet().GetPool()));
if(bResizeShapeToFitText)
{
// always reset MinWidthHeight to zero to only rely on text size and frame size
// to allow resizing being completely dependent on text size only
aSet.Put(makeSdrTextMinFrameWidthItem(0));
aSet.Put(makeSdrTextMinFrameHeightItem(0));
}
else
{
// recreate from CustomShape-specific TextBounds
const tools::Long nHDist(GetTextLeftDistance() + GetTextRightDistance());
const tools::Long nVDist(GetTextUpperDistance() + GetTextLowerDistance());
const tools::Long nTWdt(std::max(tools::Long(0), static_cast<tools::Long>(aTextBound.GetWidth() - 1 - nHDist)));
const tools::Long nTHgt(std::max(tools::Long(0), static_cast<tools::Long>(aTextBound.GetHeight() - 1 - nVDist)));
aSet.Put(makeSdrTextMinFrameWidthItem(nTWdt));
aSet.Put(makeSdrTextMinFrameHeightItem(nTHgt));
}
SetObjectItemSet(aSet);
}
void SdrObjCustomShape::NbcSetSnapRect(const tools::Rectangle& rRectangle)
{
tools::Rectangle aRectangle(rRectangle);
ImpJustifyRect(aRectangle);
setRectangle(aRectangle);
InvalidateRenderGeometry();
AdaptTextMinSize();
ImpCheckShear();
SetBoundAndSnapRectsDirty();
SetChanged();
}
void SdrObjCustomShape::SetSnapRect( const tools::Rectangle& rRect )
{
tools::Rectangle aBoundRect0;
if ( m_pUserCall )
aBoundRect0 = GetLastBoundRect();
NbcSetSnapRect( rRect );
BroadcastObjectChange();
SendUserCall(SdrUserCallType::Resize,aBoundRect0);
}
void SdrObjCustomShape::NbcSetLogicRect(const tools::Rectangle& rRectangle, bool bAdaptTextMinSize)
{
tools::Rectangle aRectangle(rRectangle);
ImpJustifyRect(aRectangle);
setRectangle(aRectangle);
InvalidateRenderGeometry();
if (bAdaptTextMinSize)
AdaptTextMinSize();
SetBoundAndSnapRectsDirty();
SetChanged();
}
void SdrObjCustomShape::SetLogicRect( const tools::Rectangle& rRect )
{
tools::Rectangle aBoundRect0;
if ( m_pUserCall )
aBoundRect0 = GetLastBoundRect();
NbcSetLogicRect(rRect);
BroadcastObjectChange();
SendUserCall(SdrUserCallType::Resize,aBoundRect0);
}
void SdrObjCustomShape::Move( const Size& rSiz )
{
if ( rSiz.Width() || rSiz.Height() )
{
tools::Rectangle aBoundRect0;
if ( m_pUserCall )
aBoundRect0 = GetLastBoundRect();
NbcMove(rSiz);
SetChanged();
BroadcastObjectChange();
SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
}
}
void SdrObjCustomShape::NbcMove( const Size& rSiz )
{
SdrTextObj::NbcMove( rSiz );
if ( mXRenderedCustomShape.is() )
{
// #i97149# the visualisation shape needs to be informed
// about change, too
mXRenderedCustomShape->ActionChanged();
mXRenderedCustomShape->NbcMove( rSiz );
}
// #i37011# adapt geometry shadow
if(mpLastShadowGeometry)
{
mpLastShadowGeometry->NbcMove( rSiz );
}
}
void SdrObjCustomShape::NbcResize( const Point& rRef, double rxFact, double ryFact )
{
// taking care of handles that should not been changed
tools::Rectangle aOld(getRectangle());
std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
SdrTextObj::NbcResize( rRef, rxFact, ryFact );
if ( ( rxFact != 1.0 ) || ( ryFact != 1.0 ) )
{
if ( rxFact < 0 )
{
SetMirroredX( !IsMirroredX() );
}
if ( ryFact < 0 )
{
SetMirroredY( !IsMirroredY() );
}
}
for (const auto& rInteraction : aInteractionHandles)
{
try
{
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X )
{
sal_Int32 nX = ( rInteraction.aPosition.X - aOld.Left() ) + getRectangle().Left();
rInteraction.xInteraction->setControllerPosition(awt::Point(nX, rInteraction.xInteraction->getPosition().Y));
}
else if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
{
sal_Int32 nX = getRectangle().Right() - (aOld.Right() - rInteraction.aPosition.X);
rInteraction.xInteraction->setControllerPosition(awt::Point(nX, rInteraction.xInteraction->getPosition().Y));
}
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
{
sal_Int32 nY = ( rInteraction.aPosition.Y - aOld.Top() ) + getRectangle().Top();
rInteraction.xInteraction->setControllerPosition(awt::Point(rInteraction.xInteraction->getPosition().X, nY));
}
}
catch ( const uno::RuntimeException& )
{
}
}
// updating fObjectRotation
Degree100 nTextObjRotation = maGeo.m_nRotationAngle;
double fAngle = toDegrees(nTextObjRotation);
if (IsMirroredX())
{
if (IsMirroredY())
m_fObjectRotation = fAngle - 180.0;
else
m_fObjectRotation = -fAngle;
}
else
{
if (IsMirroredY())
m_fObjectRotation = 180.0 - fAngle;
else
m_fObjectRotation = fAngle;
}
while (m_fObjectRotation < 0)
m_fObjectRotation += 360.0;
while (m_fObjectRotation >= 360.0)
m_fObjectRotation -= 360.0;
InvalidateRenderGeometry();
}
void SdrObjCustomShape::NbcRotate( const Point& rRef, Degree100 nAngle, double sn, double cs )
{
bool bMirroredX = IsMirroredX();
bool bMirroredY = IsMirroredY();
m_fObjectRotation = fmod( m_fObjectRotation, 360.0 );
if ( m_fObjectRotation < 0 )
m_fObjectRotation = 360 + m_fObjectRotation;
// the rotation angle for ashapes is stored in fObjectRotation, this rotation
// has to be applied to the text object (which is internally using maGeo.nAngle).
SdrTextObj::NbcRotate( getRectangle().TopLeft(), -maGeo.m_nRotationAngle, // retrieving the unrotated text object
-maGeo.mfSinRotationAngle,
maGeo.mfCosRotationAngle );
maGeo.m_nRotationAngle = 0_deg100; // resetting aGeo data
maGeo.RecalcSinCos();
Degree100 nW(static_cast<sal_Int32>( m_fObjectRotation * 100 )); // applying our object rotation
if ( bMirroredX )
nW = 36000_deg100 - nW;
if ( bMirroredY )
nW = 18000_deg100 - nW;
nW = nW % 36000_deg100;
if ( nW < 0_deg100 )
nW = 36000_deg100 + nW;
SdrTextObj::NbcRotate( getRectangle().TopLeft(), nW, // applying text rotation
sin( toRadians(nW) ),
cos( toRadians(nW) ) );
int nSwap = 0;
if ( bMirroredX )
nSwap ^= 1;
if ( bMirroredY )
nSwap ^= 1;
double fAngle = toDegrees(nAngle); // updating to our new object rotation
m_fObjectRotation = fmod( nSwap ? m_fObjectRotation - fAngle : m_fObjectRotation + fAngle, 360.0 );
if ( m_fObjectRotation < 0 )
m_fObjectRotation = 360 + m_fObjectRotation;
SdrTextObj::NbcRotate( rRef, nAngle, sn, cs ); // applying text rotation
InvalidateRenderGeometry();
}
void SdrObjCustomShape::NbcMirror( const Point& rRef1, const Point& rRef2 )
{
// TTTT: Fix for old mirroring, can be removed again in aw080
// storing horizontal and vertical flipping without modifying the rotate angle
// decompose other flipping to rotation and MirrorX.
tools::Long ndx = rRef2.X()-rRef1.X();
tools::Long ndy = rRef2.Y()-rRef1.Y();
if(!ndx) // MirroredX
{
SetMirroredX(!IsMirroredX());
SdrTextObj::NbcMirror( rRef1, rRef2 );
}
else
{
if(!ndy) // MirroredY
{
SetMirroredY(!IsMirroredY());
SdrTextObj::NbcMirror( rRef1, rRef2 );
}
else // neither horizontal nor vertical
{
SetMirroredX(!IsMirroredX());
// call parent
SdrTextObj::NbcMirror( rRef1, rRef2 );
// update fObjectRotation
Degree100 nTextObjRotation = maGeo.m_nRotationAngle;
double fAngle = toDegrees(nTextObjRotation);
bool bSingleFlip = (IsMirroredX()!= IsMirroredY());
m_fObjectRotation = fmod( bSingleFlip ? -fAngle : fAngle, 360.0 );
if ( m_fObjectRotation < 0 )
{
m_fObjectRotation = 360.0 + m_fObjectRotation;
}
}
}
InvalidateRenderGeometry();
}
void SdrObjCustomShape::Shear( const Point& rRef, Degree100 nAngle, double tn, bool bVShear )
{
SdrTextObj::Shear( rRef, nAngle, tn, bVShear );
InvalidateRenderGeometry();
}
void SdrObjCustomShape::NbcShear( const Point& rRef, Degree100 nAngle, double tn, bool bVShear )
{
// TTTT: Fix for old mirroring, can be removed again in aw080
SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
// updating fObjectRotation
Degree100 nTextObjRotation = maGeo.m_nRotationAngle;
double fAngle = toDegrees(nTextObjRotation);
if (IsMirroredX())
{
if (IsMirroredY())
m_fObjectRotation = fAngle - 180.0;
else
m_fObjectRotation = -fAngle;
}
else
{
if (IsMirroredY())
m_fObjectRotation = 180.0 - fAngle;
else
m_fObjectRotation = fAngle;
}
while (m_fObjectRotation < 0)
m_fObjectRotation += 360.0;
while (m_fObjectRotation >= 360.0)
m_fObjectRotation -= 360.0;
InvalidateRenderGeometry();
}
SdrGluePoint SdrObjCustomShape::GetVertexGluePoint(sal_uInt16 nPosNum) const
{
sal_Int32 nWdt = ImpGetLineWdt(); // #i25616#
// #i25616#
if(!LineIsOutsideGeometry())
{
nWdt++;
nWdt /= 2;
}
Point aPt;
tools::Rectangle aRectangle = getRectangle();
switch (nPosNum)
{
case 0: aPt = aRectangle.TopCenter(); aPt.AdjustY( -nWdt ); break;
case 1: aPt = aRectangle.RightCenter(); aPt.AdjustX(nWdt ); break;
case 2: aPt = aRectangle.BottomCenter(); aPt.AdjustY(nWdt ); break;
case 3: aPt = aRectangle.LeftCenter(); aPt.AdjustX( -nWdt ); break;
}
if (maGeo.m_nShearAngle != 0_deg100)
ShearPoint(aPt, aRectangle.TopLeft(), maGeo.mfTanShearAngle);
if (maGeo.m_nRotationAngle != 0_deg100)
RotatePoint(aPt, aRectangle.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
aPt-=GetSnapRect().Center();
SdrGluePoint aGP(aPt);
aGP.SetPercent(false);
return aGP;
}
// #i38892#
void SdrObjCustomShape::ImpCheckCustomGluePointsAreAdded()
{
const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
if(!pSdrObject)
return;
const SdrGluePointList* pSource = pSdrObject->GetGluePointList();
if(!(pSource && pSource->GetCount()))
return;
if(!SdrTextObj::GetGluePointList())
{
SdrTextObj::ForceGluePointList();
}
const SdrGluePointList* pList = SdrTextObj::GetGluePointList();
if(!pList)
return;
SdrGluePointList aNewList;
sal_uInt16 a;
for(a = 0; a < pSource->GetCount(); a++)
{
SdrGluePoint aCopy((*pSource)[a]);
aCopy.SetUserDefined(false);
aNewList.Insert(aCopy);
}
bool bMirroredX = IsMirroredX();
bool bMirroredY = IsMirroredY();
Degree100 nShearAngle = maGeo.m_nShearAngle;
double fTan = maGeo.mfTanShearAngle;
if (maGeo.m_nRotationAngle || nShearAngle || bMirroredX || bMirroredY)
{
tools::Polygon aPoly(getRectangle());
if( nShearAngle )
{
sal_uInt16 nPointCount=aPoly.GetSize();
for (sal_uInt16 i=0; i<nPointCount; i++)
ShearPoint(aPoly[i], getRectangle().Center(), fTan );
}
if (maGeo.m_nRotationAngle)
aPoly.Rotate( getRectangle().Center(), to<Degree10>(maGeo.m_nRotationAngle) );
tools::Rectangle aBoundRect( aPoly.GetBoundRect() );
sal_Int32 nXDiff = aBoundRect.Left() - getRectangle().Left();
sal_Int32 nYDiff = aBoundRect.Top() - getRectangle().Top();
if (nShearAngle && bMirroredX != bMirroredY)
{
nShearAngle = -nShearAngle;
fTan = -fTan;
}
Point aRef( getRectangle().GetWidth() / 2, getRectangle().GetHeight() / 2 );
for ( a = 0; a < aNewList.GetCount(); a++ )
{
SdrGluePoint& rPoint = aNewList[ a ];
Point aGlue( rPoint.GetPos() );
if ( nShearAngle )
ShearPoint( aGlue, aRef, fTan );
RotatePoint(aGlue, aRef, sin(basegfx::deg2rad(m_fObjectRotation)),
cos(basegfx::deg2rad(m_fObjectRotation)));
if ( bMirroredX )
aGlue.setX( getRectangle().GetWidth() - aGlue.X() );
if ( bMirroredY )
aGlue.setY( getRectangle().GetHeight() - aGlue.Y() );
aGlue.AdjustX( -nXDiff );
aGlue.AdjustY( -nYDiff );
rPoint.SetPos( aGlue );
}
}
for(a = 0; a < pList->GetCount(); a++)
{
const SdrGluePoint& rCandidate = (*pList)[a];
if(rCandidate.IsUserDefined())
{
aNewList.Insert(rCandidate);
}
}
// copy new list to local. This is NOT very convenient behavior, the local
// GluePointList should not be set, but we delivered by using GetGluePointList(),
// maybe on demand. Since the local object is changed here, this is assumed to
// be a result of GetGluePointList and thus the list is copied
if(m_pPlusData)
{
m_pPlusData->SetGluePoints(aNewList);
}
}
// #i38892#
const SdrGluePointList* SdrObjCustomShape::GetGluePointList() const
{
const_cast<SdrObjCustomShape*>(this)->ImpCheckCustomGluePointsAreAdded();
return SdrTextObj::GetGluePointList();
}
// #i38892#
SdrGluePointList* SdrObjCustomShape::ForceGluePointList()
{
if(SdrTextObj::ForceGluePointList())
{
ImpCheckCustomGluePointsAreAdded();
return SdrTextObj::ForceGluePointList();
}
else
{
return nullptr;
}
}
sal_uInt32 SdrObjCustomShape::GetHdlCount() const
{
const sal_uInt32 nBasicHdlCount(SdrTextObj::GetHdlCount());
return ( GetInteractionHandles().size() + nBasicHdlCount );
}
void SdrObjCustomShape::AddToHdlList(SdrHdlList& rHdlList) const
{
SdrTextObj::AddToHdlList(rHdlList);
int nCustomShapeHdlNum = 0;
for (SdrCustomShapeInteraction const & rInteraction : GetInteractionHandles())
{
if ( rInteraction.xInteraction.is() )
{
try
{
awt::Point aPosition( rInteraction.xInteraction->getPosition() );
std::unique_ptr<SdrHdl> pH(new SdrHdl( Point( aPosition.X, aPosition.Y ), SdrHdlKind::CustomShape1 ));
pH->SetPointNum( nCustomShapeHdlNum );
pH->SetObj( const_cast<SdrObjCustomShape*>(this) );
rHdlList.AddHdl(std::move(pH));
}
catch ( const uno::RuntimeException& )
{
}
}
++nCustomShapeHdlNum;
}
}
bool SdrObjCustomShape::hasSpecialDrag() const
{
return true;
}
bool SdrObjCustomShape::beginSpecialDrag(SdrDragStat& rDrag) const
{
const SdrHdl* pHdl = rDrag.GetHdl();
if(pHdl && SdrHdlKind::CustomShape1 == pHdl->GetKind())
{
rDrag.SetEndDragChangesAttributes(true);
rDrag.SetNoSnap();
}
else
{
const SdrHdl* pHdl2 = rDrag.GetHdl();
const SdrHdlKind eHdl((pHdl2 == nullptr) ? SdrHdlKind::Move : pHdl2->GetKind());
switch( eHdl )
{
case SdrHdlKind::UpperLeft :
case SdrHdlKind::Upper :
case SdrHdlKind::UpperRight :
case SdrHdlKind::Left :
case SdrHdlKind::Right :
case SdrHdlKind::LowerLeft :
case SdrHdlKind::Lower :
case SdrHdlKind::LowerRight :
case SdrHdlKind::Move :
{
break;
}
default:
{
return false;
}
}
}
return true;
}
void SdrObjCustomShape::DragResizeCustomShape( const tools::Rectangle& rNewRect )
{
tools::Rectangle aOld(getRectangle());
bool bOldMirroredX( IsMirroredX() );
bool bOldMirroredY( IsMirroredY() );
tools::Rectangle aNewRect( rNewRect );
aNewRect.Normalize();
std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
GeoStat aGeoStat( GetGeoStat() );
if ( aNewRect.TopLeft() != getRectangle().TopLeft() &&
( maGeo.m_nRotationAngle || maGeo.m_nShearAngle ) )
{
Point aNewPos( aNewRect.TopLeft() );
if ( maGeo.m_nShearAngle ) ShearPoint( aNewPos, aOld.TopLeft(), aGeoStat.mfTanShearAngle );
if ( maGeo.m_nRotationAngle ) RotatePoint(aNewPos, aOld.TopLeft(), aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle );
aNewRect.SetPos( aNewPos );
}
if (aNewRect == getRectangle())
return;
SetLogicRect( aNewRect );
InvalidateRenderGeometry();
if ( rNewRect.Left() > rNewRect.Right() )
{
Point aTop( ( GetSnapRect().Left() + GetSnapRect().Right() ) >> 1, GetSnapRect().Top() );
Point aBottom( aTop.X(), aTop.Y() + 1000 );
NbcMirror( aTop, aBottom );
}
if ( rNewRect.Top() > rNewRect.Bottom() )
{
Point aLeft( GetSnapRect().Left(), ( GetSnapRect().Top() + GetSnapRect().Bottom() ) >> 1 );
Point aRight( aLeft.X() + 1000, aLeft.Y() );
NbcMirror( aLeft, aRight );
}
for (const auto& rInteraction : aInteractionHandles)
{
try
{
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X ||
rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
{
if (rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX)
bOldMirroredX = !bOldMirroredX;
sal_Int32 nX;
if ( bOldMirroredX )
{
nX = ( rInteraction.aPosition.X - aOld.Right() );
if ( rNewRect.Left() > rNewRect.Right() )
nX = getRectangle().Left() - nX;
else
nX += getRectangle().Right();
}
else
{
nX = ( rInteraction.aPosition.X - aOld.Left() );
if ( rNewRect.Left() > rNewRect.Right() )
nX = getRectangle().Right() - nX;
else
nX += getRectangle().Left();
}
rInteraction.xInteraction->setControllerPosition(awt::Point(nX, rInteraction.xInteraction->getPosition().Y));
}
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
{
sal_Int32 nY;
if ( bOldMirroredY )
{
nY = ( rInteraction.aPosition.Y - aOld.Bottom() );
if ( rNewRect.Top() > rNewRect.Bottom() )
nY = getRectangle().Top() - nY;
else
nY += getRectangle().Bottom();
}
else
{
nY = ( rInteraction.aPosition.Y - aOld.Top() );
if ( rNewRect.Top() > rNewRect.Bottom() )
nY = getRectangle().Bottom() - nY;
else
nY += getRectangle().Top();
}
rInteraction.xInteraction->setControllerPosition(awt::Point(rInteraction.xInteraction->getPosition().X, nY));
}
}
catch ( const uno::RuntimeException& )
{
}
}
}
void SdrObjCustomShape::DragMoveCustomShapeHdl( const Point& rDestination,
const sal_uInt16 nCustomShapeHdlNum, bool bMoveCalloutRectangle )
{
std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
if ( nCustomShapeHdlNum >= aInteractionHandles.size() )
return;
SdrCustomShapeInteraction aInteractionHandle( aInteractionHandles[ nCustomShapeHdlNum ] );
if ( !aInteractionHandle.xInteraction.is() )
return;
try
{
awt::Point aPt( rDestination.X(), rDestination.Y() );
if ( aInteractionHandle.nMode & CustomShapeHandleModes::MOVE_SHAPE && bMoveCalloutRectangle )
{
sal_Int32 nXDiff = aPt.X - aInteractionHandle.aPosition.X;
sal_Int32 nYDiff = aPt.Y - aInteractionHandle.aPosition.Y;
moveRectangle(nXDiff, nYDiff);
moveOutRectangle(nXDiff, nYDiff);
maSnapRect.Move( nXDiff, nYDiff );
SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
InvalidateRenderGeometry();
for (const auto& rInteraction : aInteractionHandles)
{
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
{
if ( rInteraction.xInteraction.is() )
rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
}
}
}
aInteractionHandle.xInteraction->setControllerPosition( aPt );
}
catch ( const uno::RuntimeException& )
{
}
}
bool SdrObjCustomShape::applySpecialDrag(SdrDragStat& rDrag)
{
const SdrHdl* pHdl = rDrag.GetHdl();
const SdrHdlKind eHdl((pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind());
switch(eHdl)
{
case SdrHdlKind::CustomShape1 :
{
rDrag.SetEndDragChangesGeoAndAttributes(true);
DragMoveCustomShapeHdl( rDrag.GetNow(), static_cast<sal_uInt16>(pHdl->GetPointNum()), !rDrag.GetDragMethod()->IsShiftPressed() );
SetBoundAndSnapRectsDirty();
InvalidateRenderGeometry();
SetChanged();
break;
}
case SdrHdlKind::UpperLeft :
case SdrHdlKind::Upper :
case SdrHdlKind::UpperRight :
case SdrHdlKind::Left :
case SdrHdlKind::Right :
case SdrHdlKind::LowerLeft :
case SdrHdlKind::Lower :
case SdrHdlKind::LowerRight :
{
DragResizeCustomShape( ImpDragCalcRect(rDrag) );
break;
}
case SdrHdlKind::Move :
{
Move(Size(rDrag.GetDX(), rDrag.GetDY()));
break;
}
default: break;
}
return true;
}
void SdrObjCustomShape::DragCreateObject( SdrDragStat& rStat )
{
tools::Rectangle aRect1;
rStat.TakeCreateRect( aRect1 );
std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
constexpr sal_uInt32 nDefaultObjectSizeWidth = 3000; // default width from SDOptions ?
constexpr sal_uInt32 nDefaultObjectSizeHeight= 3000;
if ( ImpVerticalSwitch( *this ) )
{
SetMirroredX( aRect1.Left() > aRect1.Right() );
aRect1 = tools::Rectangle( rStat.GetNow(), Size( nDefaultObjectSizeWidth, nDefaultObjectSizeHeight ) );
// subtracting the horizontal difference of the latest handle from shape position
if ( !aInteractionHandles.empty() )
{
sal_Int32 nHandlePos = aInteractionHandles[ aInteractionHandles.size() - 1 ].xInteraction->getPosition().X;
aRect1.Move(getRectangle().Left() - nHandlePos, 0);
}
}
ImpJustifyRect( aRect1 );
rStat.SetActionRect( aRect1 );
setRectangle(aRect1);
SetBoundAndSnapRectsDirty();
for (const auto& rInteraction : aInteractionHandles)
{
try
{
if ( rInteraction.nMode & CustomShapeHandleModes::CREATE_FIXED )
rInteraction.xInteraction->setControllerPosition( awt::Point( rStat.GetStart().X(), rStat.GetStart().Y() ) );
}
catch ( const uno::RuntimeException& )
{
}
}
SetBoundRectDirty();
m_bSnapRectDirty=true;
}
bool SdrObjCustomShape::MovCreate(SdrDragStat& rStat)
{
SdrView* pView = rStat.GetView(); // #i37448#
if( pView && pView->IsSolidDragging() )
{
InvalidateRenderGeometry();
}
DragCreateObject( rStat );
SetBoundAndSnapRectsDirty();
return true;
}
bool SdrObjCustomShape::EndCreate( SdrDragStat& rStat, SdrCreateCmd eCmd )
{
DragCreateObject( rStat );
AdaptTextMinSize();
SetBoundAndSnapRectsDirty();
return ( eCmd == SdrCreateCmd::ForceEnd || rStat.GetPointCount() >= 2 );
}
basegfx::B2DPolyPolygon SdrObjCustomShape::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
{
return GetLineGeometry( false );
}
// in context with the SdrObjCustomShape the SdrTextAutoGrowHeightItem == true -> Resize Shape to fit text,
// the SdrTextAutoGrowWidthItem == true -> Word wrap text in Shape
bool SdrObjCustomShape::IsAutoGrowHeight() const
{
const SfxItemSet& rSet = GetMergedItemSet();
bool bIsAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
if ( bIsAutoGrowHeight && IsVerticalWriting() )
bIsAutoGrowHeight = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue();
return bIsAutoGrowHeight;
}
bool SdrObjCustomShape::IsAutoGrowWidth() const
{
const SfxItemSet& rSet = GetMergedItemSet();
bool bIsAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
if ( bIsAutoGrowWidth && !IsVerticalWriting() )
bIsAutoGrowWidth = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue();
return bIsAutoGrowWidth;
}
/* The following method is identical to the SdrTextObj::SetVerticalWriting method, the only difference
is that the SdrAutoGrowWidthItem and SdrAutoGrowHeightItem are not exchanged if the vertical writing
mode has been changed */
void SdrObjCustomShape::SetVerticalWriting( bool bVertical )
{
ForceOutlinerParaObject();
OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
DBG_ASSERT( pOutlinerParaObject, "SdrTextObj::SetVerticalWriting() without OutlinerParaObject!" );
if( !pOutlinerParaObject ||
(pOutlinerParaObject->IsEffectivelyVertical() == bVertical) )
return;
// get item settings
const SfxItemSet& rSet = GetObjectItemSet();
// Also exchange horizontal and vertical adjust items
SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
// rescue object size, SetSnapRect below expects logic rect,
// not snap rect.
tools::Rectangle aObjectRect = GetLogicRect();
// prepare ItemSet to set exchanged width and height items
SfxItemSet aNewSet(SfxItemSet::makeFixedSfxItemSet<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
// Expanded item ranges to also support horizontal and vertical adjust.
SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST> (*rSet.GetPool()));
aNewSet.Put(rSet);
// Exchange horizontal and vertical adjusts
switch(eVert)
{
case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
}
switch(eHorz)
{
case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
}
pOutlinerParaObject = GetOutlinerParaObject();
if ( pOutlinerParaObject )
pOutlinerParaObject->SetVertical(bVertical);
SetObjectItemSet( aNewSet );
// restore object size
SetSnapRect(aObjectRect);
}
void SdrObjCustomShape::SuggestTextFrameSize(Size aSuggestedTextFrameSize)
{
m_aSuggestedTextFrameSize = aSuggestedTextFrameSize;
}
bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight(tools::Rectangle& rR, bool bHgt, bool bWdt) const
{
// Either we have text or the application has native text and suggested its size to us.
bool bHasText = HasText() || !m_aSuggestedTextFrameSize.IsEmpty();
if ( bHasText && !rR.IsEmpty() )
{
bool bWdtGrow=bWdt && IsAutoGrowWidth();
bool bHgtGrow=bHgt && IsAutoGrowHeight();
if ( bWdtGrow || bHgtGrow )
{
tools::Rectangle aR0(rR);
tools::Long nHgt=0,nMinHgt=0,nMaxHgt=0;
tools::Long nWdt=0,nMinWdt=0,nMaxWdt=0;
Size aSiz(rR.GetSize()); aSiz.AdjustWidth( -1 ); aSiz.AdjustHeight( -1 );
Size aMaxSiz(100000,100000);
Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
if (bWdtGrow)
{
nMinWdt=GetMinTextFrameWidth();
nMaxWdt=GetMaxTextFrameWidth();
if (nMaxWdt==0 || nMaxWdt>aMaxSiz.Width()) nMaxWdt=aMaxSiz.Width();
if (nMinWdt<=0) nMinWdt=1;
aSiz.setWidth(nMaxWdt );
}
if (bHgtGrow)
{
nMinHgt=GetMinTextFrameHeight();
nMaxHgt=GetMaxTextFrameHeight();
if (nMaxHgt==0 || nMaxHgt>aMaxSiz.Height()) nMaxHgt=aMaxSiz.Height();
if (nMinHgt<=0) nMinHgt=1;
aSiz.setHeight(nMaxHgt );
}
tools::Long nHDist=GetTextLeftDistance()+GetTextRightDistance();
tools::Long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
aSiz.AdjustWidth( -nHDist );
aSiz.AdjustHeight( -nVDist );
if ( aSiz.Width() < 2 )
aSiz.setWidth( 2 ); // minimum size=2
if ( aSiz.Height() < 2 )
aSiz.setHeight( 2 ); // minimum size=2
if (HasText())
{
if(mpEditingOutliner)
{
mpEditingOutliner->SetMaxAutoPaperSize( aSiz );
if (bWdtGrow)
{
Size aSiz2(mpEditingOutliner->CalcTextSize());
nWdt=aSiz2.Width()+1; // a little more tolerance
if (bHgtGrow) nHgt=aSiz2.Height()+1; // a little more tolerance
} else
{
nHgt=mpEditingOutliner->GetTextHeight()+1; // a little more tolerance
}
}
else
{
Outliner& rOutliner=ImpGetDrawOutliner();
rOutliner.SetPaperSize(aSiz);
// TODO: add the optimization with bPortionInfoChecked again.
OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
if( pOutlinerParaObject != nullptr )
{
rOutliner.SetFixedCellHeight(
GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
rOutliner.SetText(*pOutlinerParaObject);
}
rOutliner.SetUpdateLayout(true);
if ( bWdtGrow )
{
Size aSiz2(rOutliner.CalcTextSize());
nWdt=aSiz2.Width()+1; // a little more tolerance
if ( bHgtGrow )
nHgt=aSiz2.Height()+1; // a little more tolerance
}
else
{
nHgt = rOutliner.GetTextHeight()+1; // a little more tolerance
sal_Int16 nColumns = GetMergedItem(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue();
if (bHgtGrow && nColumns > 1)
{
// Both 'resize shape to fix text' and multiple columns are enabled. The
// first means a dynamic height, the second expects a fixed height.
// Resolve this conflict by going with the original height.
nHgt = rR.getOpenHeight();
}
}
// cleanup outliner
rOutliner.Clear();
rOutliner.SetFixedCellHeight(false);
}
}
else
{
nHgt = m_aSuggestedTextFrameSize.Height();
nWdt = m_aSuggestedTextFrameSize.Width();
}
if ( nWdt < nMinWdt )
nWdt = nMinWdt;
if ( nWdt > nMaxWdt )
nWdt = nMaxWdt;
nWdt += nHDist;
if ( nWdt < 1 )
nWdt = 1; // nHDist may also be negative
if ( nHgt < nMinHgt )
nHgt = nMinHgt;
if ( nHgt > nMaxHgt )
nHgt = nMaxHgt;
nHgt+=nVDist;
if ( nHgt < 1 )
nHgt = 1; // nVDist may also be negative
tools::Long nWdtGrow = nWdt-(rR.Right()-rR.Left());
tools::Long nHgtGrow = nHgt-(rR.Bottom()-rR.Top());
if ( nWdtGrow == 0 )
bWdtGrow = false;
if ( nHgtGrow == 0 )
bHgtGrow=false;
if ( bWdtGrow || bHgtGrow || !m_aSuggestedTextFrameSize.IsEmpty())
{
if ( bWdtGrow || m_aSuggestedTextFrameSize.Width() )
{
SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
if (m_aSuggestedTextFrameSize.Width())
{
rR.SetRight(rR.Left() + m_aSuggestedTextFrameSize.Width());
}
else if ( eHAdj == SDRTEXTHORZADJUST_LEFT )
rR.AdjustRight(nWdtGrow );
else if ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
rR.AdjustLeft( -nWdtGrow );
else
{
tools::Long nWdtGrow2=nWdtGrow/2;
rR.AdjustLeft( -nWdtGrow2 );
rR.SetRight(rR.Left()+nWdt );
}
}
if ( bHgtGrow || m_aSuggestedTextFrameSize.Height() )
{
SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
if (m_aSuggestedTextFrameSize.Height())
{
rR.SetBottom(rR.Top() + m_aSuggestedTextFrameSize.Height());
}
else if ( eVAdj == SDRTEXTVERTADJUST_TOP )
rR.AdjustBottom(nHgtGrow );
else if ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
rR.AdjustTop( -nHgtGrow );
else
{
tools::Long nHgtGrow2=nHgtGrow/2;
rR.AdjustTop( -nHgtGrow2 );
rR.SetBottom(rR.Top()+nHgt );
}
}
if ( maGeo.m_nRotationAngle )
{
Point aD1(rR.TopLeft());
aD1-=aR0.TopLeft();
Point aD2(aD1);
RotatePoint(aD2,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
aD2-=aD1;
rR.Move(aD2.X(),aD2.Y());
}
return true;
}
}
}
return false;
}
tools::Rectangle SdrObjCustomShape::ImpCalculateTextFrame( const bool bHgt, const bool bWdt )
{
tools::Rectangle aReturnValue;
tools::Rectangle aOldTextRect(getRectangle()); // <- initial text rectangle
tools::Rectangle aNewTextRect(getRectangle()); // <- new text rectangle returned from the custom shape renderer,
GetTextBounds( aNewTextRect ); // it depends to the current logical shape size
tools::Rectangle aAdjustedTextRect( aNewTextRect ); // <- new text rectangle is being tested by AdjustTextFrameWidthAndHeight to ensure
if ( AdjustTextFrameWidthAndHeight( aAdjustedTextRect, bHgt, bWdt ) ) // that the new text rectangle is matching the current text size from the outliner
{
if (aAdjustedTextRect != aNewTextRect && aOldTextRect != aAdjustedTextRect &&
aNewTextRect.GetWidth() && aNewTextRect.GetHeight())
{
aReturnValue = getRectangle();
double fXScale = static_cast<double>(aOldTextRect.GetWidth()) / static_cast<double>(aNewTextRect.GetWidth());
double fYScale = static_cast<double>(aOldTextRect.GetHeight()) / static_cast<double>(aNewTextRect.GetHeight());
double fRightDiff = static_cast<double>( aAdjustedTextRect.Right() - aNewTextRect.Right() ) * fXScale;
double fLeftDiff = static_cast<double>( aAdjustedTextRect.Left() - aNewTextRect.Left() ) * fXScale;
double fTopDiff = static_cast<double>( aAdjustedTextRect.Top() - aNewTextRect.Top() ) * fYScale;
double fBottomDiff= static_cast<double>( aAdjustedTextRect.Bottom()- aNewTextRect.Bottom()) * fYScale;
aReturnValue.AdjustLeft(static_cast<sal_Int32>(fLeftDiff) );
aReturnValue.AdjustRight(static_cast<sal_Int32>(fRightDiff) );
aReturnValue.AdjustTop(static_cast<sal_Int32>(fTopDiff) );
aReturnValue.AdjustBottom(static_cast<sal_Int32>(fBottomDiff) );
}
}
return aReturnValue;
}
bool SdrObjCustomShape::NbcAdjustTextFrameWidthAndHeight(bool bHgt, bool bWdt)
{
tools::Rectangle aNewTextRect = ImpCalculateTextFrame(bHgt, bWdt);
const bool bRet = !aNewTextRect.IsEmpty() && aNewTextRect != getRectangle();
if (bRet && !mbAdjustingTextFrameWidthAndHeight)
{
mbAdjustingTextFrameWidthAndHeight = true;
// taking care of handles that should not been changed
std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
setRectangle(aNewTextRect);
SetBoundAndSnapRectsDirty();
SetChanged();
for (const auto& rInteraction : aInteractionHandles)
{
try
{
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
}
catch ( const uno::RuntimeException& )
{
}
}
InvalidateRenderGeometry();
mbAdjustingTextFrameWidthAndHeight = false;
}
return bRet;
}
bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight()
{
tools::Rectangle aNewTextRect = ImpCalculateTextFrame( true/*bHgt*/, true/*bWdt*/ );
bool bRet = !aNewTextRect.IsEmpty() && ( aNewTextRect != getRectangle());
if ( bRet )
{
tools::Rectangle aBoundRect0;
if ( m_pUserCall )
aBoundRect0 = GetCurrentBoundRect();
// taking care of handles that should not been changed
std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
setRectangle(aNewTextRect);
SetBoundAndSnapRectsDirty();
for (const auto& rInteraction : aInteractionHandles)
{
try
{
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
}
catch ( const uno::RuntimeException& )
{
}
}
InvalidateRenderGeometry();
SetChanged();
BroadcastObjectChange();
SendUserCall(SdrUserCallType::Resize,aBoundRect0);
}
return bRet;
}
void SdrObjCustomShape::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
{
tools::Rectangle aViewInit;
TakeTextAnchorRect( aViewInit );
if (maGeo.m_nRotationAngle)
{
Point aCenter(aViewInit.Center());
aCenter-=aViewInit.TopLeft();
Point aCenter0(aCenter);
RotatePoint(aCenter, Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
aCenter-=aCenter0;
aViewInit.Move(aCenter.X(),aCenter.Y());
}
Size aAnkSiz(aViewInit.GetSize());
aAnkSiz.AdjustWidth( -1 ); aAnkSiz.AdjustHeight( -1 ); // because GetSize() adds 1
Size aMaxSiz(1000000,1000000);
{
Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
}
SdrTextHorzAdjust eHAdj(GetTextHorizontalAdjust());
SdrTextVertAdjust eVAdj(GetTextVerticalAdjust());
tools::Long nMinWdt = GetMinTextFrameWidth();
tools::Long nMinHgt = GetMinTextFrameHeight();
tools::Long nMaxWdt = GetMaxTextFrameWidth();
tools::Long nMaxHgt = GetMaxTextFrameHeight();
if (nMinWdt<1) nMinWdt=1;
if (nMinHgt<1) nMinHgt=1;
if ( nMaxWdt == 0 || nMaxWdt > aMaxSiz.Width() )
nMaxWdt = aMaxSiz.Width();
if ( nMaxHgt == 0 || nMaxHgt > aMaxSiz.Height() )
nMaxHgt=aMaxSiz.Height();
if (GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue())
{
if ( IsVerticalWriting() )
{
nMaxHgt = aAnkSiz.Height();
nMinHgt = nMaxHgt;
}
else
{
nMaxWdt = aAnkSiz.Width();
nMinWdt = nMaxWdt;
}
}
Size aPaperMax(nMaxWdt, nMaxHgt);
Size aPaperMin(nMinWdt, nMinHgt);
if ( pViewMin )
{
*pViewMin = aViewInit;
tools::Long nXFree = aAnkSiz.Width() - aPaperMin.Width();
if ( eHAdj == SDRTEXTHORZADJUST_LEFT )
pViewMin->AdjustRight( -nXFree );
else if ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
pViewMin->AdjustLeft(nXFree );
else { pViewMin->AdjustLeft(nXFree / 2 ); pViewMin->SetRight( pViewMin->Left() + aPaperMin.Width() ); }
tools::Long nYFree = aAnkSiz.Height() - aPaperMin.Height();
if ( eVAdj == SDRTEXTVERTADJUST_TOP )
pViewMin->AdjustBottom( -nYFree );
else if ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
pViewMin->AdjustTop(nYFree );
else { pViewMin->AdjustTop(nYFree / 2 ); pViewMin->SetBottom( pViewMin->Top() + aPaperMin.Height() ); }
}
if( IsVerticalWriting() )
aPaperMin.setWidth( 0 );
else
aPaperMin.setHeight( 0 );
if( eHAdj != SDRTEXTHORZADJUST_BLOCK )
aPaperMin.setWidth(0 );
// For complete vertical adjust support, set paper min height to 0, here.
if(SDRTEXTVERTADJUST_BLOCK != eVAdj )
aPaperMin.setHeight( 0 );
if (pPaperMin!=nullptr) *pPaperMin=aPaperMin;
if (pPaperMax!=nullptr) *pPaperMax=aPaperMax;
if (pViewInit!=nullptr) *pViewInit=aViewInit;
}
void SdrObjCustomShape::EndTextEdit( SdrOutliner& rOutl )
{
if (!getDiagramDataModelID().isEmpty())
{
std::shared_ptr< svx::diagram::DiagramHelper_svx > pIDiagramHelper(getDiagramHelper());
if (pIDiagramHelper)
{
// try to do the needed changes at the associated DiagramData model
pIDiagramHelper->TextInformationChange();
}
}
SdrTextObj::EndTextEdit( rOutl );
InvalidateRenderGeometry();
}
void SdrObjCustomShape::TakeTextAnchorRect( tools::Rectangle& rAnchorRect ) const
{
if ( GetTextBounds( rAnchorRect ) )
{
Point aRotateRef( maSnapRect.Center() );
const double fExtraTextRotation(GetExtraTextRotation());
AdjustRectToTextDistance(rAnchorRect, fExtraTextRotation);
if ( rAnchorRect.GetWidth() < 2 )
rAnchorRect.SetRight( rAnchorRect.Left() + 1 ); // minimal width is 2
if ( rAnchorRect.GetHeight() < 2 )
rAnchorRect.SetBottom( rAnchorRect.Top() + 1 ); // minimal height is 2
if (maGeo.m_nRotationAngle)
{
Point aP( rAnchorRect.TopLeft() );
RotatePoint(aP, aRotateRef, maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
rAnchorRect.SetPos( aP );
}
}
else
SdrTextObj::TakeTextAnchorRect( rAnchorRect );
}
void SdrObjCustomShape::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
tools::Rectangle* pAnchorRect, bool /*bLineWidth*/) const
{
tools::Rectangle aAnkRect; // Rect in which we anchor
TakeTextAnchorRect(aAnkRect);
SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
EEControlBits nStat0=rOutliner.GetControlWord();
Size aNullSize;
rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
rOutliner.SetMinAutoPaperSize(aNullSize);
sal_Int32 nMaxAutoPaperWidth = 1000000;
sal_Int32 nMaxAutoPaperHeight= 1000000;
tools::Long nAnkWdt=aAnkRect.GetWidth();
tools::Long nAnkHgt=aAnkRect.GetHeight();
if (GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue())
{
if ( IsVerticalWriting() )
nMaxAutoPaperHeight = nAnkHgt;
else
nMaxAutoPaperWidth = nAnkWdt;
}
if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
{
rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
}
if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
{
rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
}
rOutliner.SetMaxAutoPaperSize( Size( nMaxAutoPaperWidth, nMaxAutoPaperHeight ) );
rOutliner.SetPaperSize( aNullSize );
// put text into the Outliner - if necessary the use the text from the EditOutliner
std::optional<OutlinerParaObject> pPara;
if (GetOutlinerParaObject())
pPara = *GetOutlinerParaObject();
if (mpEditingOutliner && !bNoEditText)
pPara=mpEditingOutliner->CreateParaObject();
if (pPara)
{
bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
const SdrTextObj* pTestObj = rOutliner.GetTextObj();
if( !pTestObj || !bHitTest || pTestObj != this ||
pTestObj->GetOutlinerParaObject() != GetOutlinerParaObject() )
{
if( bHitTest )
rOutliner.SetTextObj( this );
rOutliner.SetUpdateLayout(true);
rOutliner.SetText(*pPara);
}
}
else
{
rOutliner.SetTextObj( nullptr );
}
rOutliner.SetUpdateLayout(true);
rOutliner.SetControlWord(nStat0);
SdrText* pText = getActiveText();
if( pText )
pText->CheckPortionInfo( rOutliner );
Point aTextPos(aAnkRect.TopLeft());
Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() has a little added tolerance, no?
// For draw objects containing text correct horizontal/vertical alignment if text is bigger
// than the object itself. Without that correction, the text would always be
// formatted to the left edge (or top edge when vertical) of the draw object.
if( !IsTextFrame() )
{
if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
{
// Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
// else the alignment is wanted.
if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
{
SvxAdjust eAdjust = GetObjectItemSet().Get(EE_PARA_JUST).GetAdjust();
switch (eAdjust)
{
case SvxAdjust::Left: eHAdj = SDRTEXTHORZADJUST_LEFT; break;
case SvxAdjust::Right: eHAdj = SDRTEXTHORZADJUST_RIGHT; break;
case SvxAdjust::Center: eHAdj = SDRTEXTHORZADJUST_CENTER; break;
default: break;
}
}
}
if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
{
// Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
// else the alignment is wanted.
if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
{
eVAdj = SDRTEXTVERTADJUST_CENTER;
}
}
}
if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
{
tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
if (eHAdj==SDRTEXTHORZADJUST_CENTER)
aTextPos.AdjustX(nFreeWdt/2 );
if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
aTextPos.AdjustX(nFreeWdt );
}
if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
{
tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
if (eVAdj==SDRTEXTVERTADJUST_CENTER)
aTextPos.AdjustY(nFreeHgt/2 );
if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
aTextPos.AdjustY(nFreeHgt );
}
if (maGeo.m_nRotationAngle != 0_deg100)
RotatePoint(aTextPos,aAnkRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
if (pAnchorRect)
*pAnchorRect=aAnkRect;
// using rTextRect together with ContourFrame doesn't always work correctly
rTextRect=tools::Rectangle(aTextPos,aTextSiz);
}
void SdrObjCustomShape::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject, bool bAdjustTextFrameWidthAndHeight)
{
SdrTextObj::NbcSetOutlinerParaObject( std::move(pTextObject), bAdjustTextFrameWidthAndHeight );
SetBoundRectDirty();
SetBoundAndSnapRectsDirty(true);
InvalidateRenderGeometry();
}
rtl::Reference<SdrObject> SdrObjCustomShape::implCloneSdrObject(SdrModel& rTargetModel) const
{
return new SdrObjCustomShape(rTargetModel, *this);
}
OUString SdrObjCustomShape::TakeObjNameSingul() const
{
OUString sName(SvxResId(STR_ObjNameSingulCUSTOMSHAPE));
OUString aNm(GetName());
if (!aNm.isEmpty())
sName += " '" + aNm + "'";
return sName;
}
OUString SdrObjCustomShape::TakeObjNamePlural() const
{
return SvxResId(STR_ObjNamePluralCUSTOMSHAPE);
}
basegfx::B2DPolyPolygon SdrObjCustomShape::TakeXorPoly() const
{
return GetLineGeometry( false );
}
basegfx::B2DPolyPolygon SdrObjCustomShape::TakeContour() const
{
const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
if ( pSdrObject )
return pSdrObject->TakeContour();
return basegfx::B2DPolyPolygon();
}
rtl::Reference<SdrObject> SdrObjCustomShape::DoConvertToPolyObj(bool bBezier, bool bAddText) const
{
// #i37011#
rtl::Reference<SdrObject> pRetval;
if ( !mXRenderedCustomShape.is() )
{
// force CustomShape
GetSdrObjectFromCustomShape();
}
SdrObject* pRenderedCustomShape = mXRenderedCustomShape.get();
if ( pRenderedCustomShape )
{
// Clone to same SdrModel
rtl::Reference<SdrObject> pCandidate(pRenderedCustomShape->CloneSdrObject(pRenderedCustomShape->getSdrModelFromSdrObject()));
DBG_ASSERT(pCandidate, "SdrObjCustomShape::DoConvertToPolyObj: Could not clone SdrObject (!)");
pRetval = pCandidate->DoConvertToPolyObj(bBezier, bAddText);
pCandidate.clear();
if(pRetval)
{
const bool bShadow(GetMergedItem(SDRATTR_SHADOW).GetValue());
if(bShadow)
{
pRetval->SetMergedItem(makeSdrShadowItem(true));
}
}
if(bAddText && HasText() && !IsTextPath())
{
pRetval = ImpConvertAddText(std::move(pRetval), bBezier);
}
}
return pRetval;
}
void SdrObjCustomShape::InternalSetStyleSheet( SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, bool bBroadcast, bool bAdjustTextFrameWidthAndHeight )
{
// #i40944#
InvalidateRenderGeometry();
SdrObject::InternalSetStyleSheet( pNewStyleSheet, bDontRemoveHardAttr, bBroadcast, bAdjustTextFrameWidthAndHeight );
}
void SdrObjCustomShape::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
{
// call parent
SdrTextObj::handlePageChange(pOldPage, pNewPage);
if(nullptr != pNewPage)
{
// invalidating rectangles by SetRectsDirty is not sufficient,
// AdjustTextFrameWidthAndHeight() also has to be made, both
// actions are done by NbcSetSnapRect
tools::Rectangle aRectangle(getRectangle()); //creating temporary rectangle #i61108#
NbcSetSnapRect(aRectangle);
}
}
std::unique_ptr<SdrObjGeoData> SdrObjCustomShape::NewGeoData() const
{
return std::make_unique<SdrAShapeObjGeoData>();
}
void SdrObjCustomShape::SaveGeoData(SdrObjGeoData& rGeo) const
{
SdrTextObj::SaveGeoData( rGeo );
SdrAShapeObjGeoData& rAGeo=static_cast<SdrAShapeObjGeoData&>(rGeo);
rAGeo.fObjectRotation = m_fObjectRotation;
rAGeo.bMirroredX = IsMirroredX();
rAGeo.bMirroredY = IsMirroredY();
const uno::Any* pAny = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ).GetPropertyValueByName( u"AdjustmentValues"_ustr );
if ( pAny )
*pAny >>= rAGeo.aAdjustmentSeq;
}
void SdrObjCustomShape::RestoreGeoData(const SdrObjGeoData& rGeo)
{
SdrTextObj::RestoreGeoData( rGeo );
const SdrAShapeObjGeoData& rAGeo=static_cast<const SdrAShapeObjGeoData&>(rGeo);
m_fObjectRotation = rAGeo.fObjectRotation;
SetMirroredX( rAGeo.bMirroredX );
SetMirroredY( rAGeo.bMirroredY );
SdrCustomShapeGeometryItem rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
beans::PropertyValue aPropVal;
aPropVal.Name = "AdjustmentValues";
aPropVal.Value <<= rAGeo.aAdjustmentSeq;
rGeometryItem.SetPropertyValue( aPropVal );
SetMergedItem( rGeometryItem );
InvalidateRenderGeometry();
}
void SdrObjCustomShape::AdjustToMaxRect(const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */)
{
SAL_INFO_IF(bShrinkOnly, "svx", "Case bShrinkOnly == true is not implemented yet.");
if (rMaxRect.IsEmpty() || rMaxRect == GetSnapRect())
return;
// Get a matrix, that would produce the existing shape, when applied to a unit square
basegfx::B2DPolyPolygon aPolyPolygon; //not used, but formal needed
basegfx::B2DHomMatrix aMatrix;
TRGetBaseGeometry(aMatrix, aPolyPolygon);
// Using TRSetBaseGeometry(aMatrix, aPolyPolygon) would regenerate the current shape. But
// applying aMatrix to a unit square will not generate the current shape. Scaling,
// rotation and translation are correct, but shear angle has wrong sign. So break up
// matrix and create a mathematically correct new one.
basegfx::B2DTuple aScale;
basegfx::B2DTuple aTranslate;
double fRotate, fShearX;
aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
basegfx::B2DHomMatrix aMathMatrix;
aMathMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
aScale,
-fShearX,
fRotate,
aTranslate);
// Calculate scaling factors from size of the transformed unit polygon as ersatz for the not
// usable current snap rectangle.
basegfx::B2DPolygon aB2DPolygon(basegfx::utils::createUnitPolygon());
aB2DPolygon.transform(aMathMatrix);
basegfx::B2DRange aB2DRange(aB2DPolygon.getB2DRange());
double fPolygonWidth = aB2DRange.getWidth();
if (fPolygonWidth == 0)
fPolygonWidth = 1;
double fPolygonHeight = aB2DRange.getHeight();
if (fPolygonHeight == 0)
fPolygonHeight = 1;
const double aFactorX = static_cast<double>(rMaxRect.GetWidth()) / fPolygonWidth;
const double aFactorY = static_cast<double>(rMaxRect.GetHeight()) / fPolygonHeight;
// Generate matrix, that would produce the desired rMaxRect when applied to unit square
aMathMatrix.scale(aFactorX, aFactorY);
aB2DPolygon = basegfx::utils::createUnitPolygon();
aB2DPolygon.transform(aMathMatrix);
aB2DRange = aB2DPolygon.getB2DRange();
const double fPolygonLeft = aB2DRange.getMinX();
const double fPolygonTop = aB2DRange.getMinY();
aMathMatrix.translate(rMaxRect.Left() - fPolygonLeft, rMaxRect.Top() - fPolygonTop);
// Create a Matrix from aMathMatrix, which is usable with TRSetBaseGeometry
aMathMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
aMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
aScale,
-fShearX,
fRotate,
aTranslate);
// Now use TRSetBaseGeometry to actually perform scale, shear, rotate and translate
// on the shape. That considers gluepoints, interaction handles and text area, and includes
// setting rectangles dirty and broadcast.
TRSetBaseGeometry(aMatrix, aPolyPolygon);
}
void SdrObjCustomShape::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
{
tools::Rectangle aBoundRect0;
if (m_pUserCall != nullptr)
aBoundRect0 = GetLastBoundRect();
// The shape might have already flipping in its enhanced geometry. LibreOffice applies
// such after all transformations. We remove it, but remember it to apply them later.
bool bIsMirroredX = IsMirroredX();
bool bIsMirroredY = IsMirroredY();
if (bIsMirroredX || bIsMirroredY)
{
Point aCurrentCenter = GetSnapRect().Center();
if (bIsMirroredX) // mirror on the y-axis
{
NbcMirror(aCurrentCenter, Point(aCurrentCenter.X(), aCurrentCenter.Y() + 1000));
}
if (bIsMirroredY) // mirror on the x-axis
{
NbcMirror(aCurrentCenter, Point(aCurrentCenter.X() + 1000, aCurrentCenter.Y()));
}
}
// break up matrix
basegfx::B2DTuple aScale;
basegfx::B2DTuple aTranslate;
double fRotate, fShearX;
rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
// reset object shear and rotations
m_fObjectRotation = 0.0;
maGeo.m_nRotationAngle = 0_deg100;
maGeo.RecalcSinCos();
maGeo.m_nShearAngle = 0_deg100;
maGeo.RecalcTan();
// if anchor is used, make position relative to it
if(getSdrModelFromSdrObject().IsWriter())
{
if(GetAnchorPos().X() || GetAnchorPos().Y())
{
aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
}
}
// scale
Size aSize(basegfx::fround<tools::Long>(fabs(aScale.getX())),
basegfx::fround<tools::Long>(fabs(aScale.getY())));
// fdo#47434 We need a valid rectangle here
if( !aSize.Height() ) aSize.setHeight( 1 );
if( !aSize.Width() ) aSize.setWidth( 1 );
tools::Rectangle aBaseRect(Point(), aSize);
NbcSetLogicRect(aBaseRect);
// Apply flipping from Matrix, which is a transformation relative to origin
if (aScale.getX() < 0.0)
NbcMirror(Point(0, 0), Point(0, 1000)); // mirror on the y-axis
if (aScale.getY() < 0.0)
NbcMirror(Point(0, 0), Point(1000, 0)); // mirror on the x-axis
// shear?
if(!basegfx::fTools::equalZero(fShearX))
{
GeoStat aGeoStat;
// #i123181# The fix for #121932# here was wrong, the trunk version does not correct the
// mirrored shear values, neither at the object level, nor on the API or XML level. Taking
// back the mirroring of the shear angle
aGeoStat.m_nShearAngle = Degree100(basegfx::fround(basegfx::rad2deg<100>(atan(fShearX))));
aGeoStat.RecalcTan();
NbcShear(Point(), aGeoStat.m_nShearAngle, aGeoStat.mfTanShearAngle, false);
}
// rotation?
if(!basegfx::fTools::equalZero(fRotate))
{
GeoStat aGeoStat;
// #i78696#
// fRotate is mathematically correct, but aGeoStat.nRotationAngle is
// mirrored -> mirror value here
aGeoStat.m_nRotationAngle = NormAngle36000(Degree100(basegfx::fround(-basegfx::rad2deg<100>(fRotate))));
aGeoStat.RecalcSinCos();
NbcRotate(Point(), aGeoStat.m_nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
}
// translate?
if (Size move(basegfx::fround<tools::Long>(aTranslate.getX()),
basegfx::fround<tools::Long>(aTranslate.getY()));
move.Width() || move.Height())
{
NbcMove(move);
}
// Apply flipping from enhanced geometry at center of the shape.
if (!(bIsMirroredX || bIsMirroredY))
return;
// create mathematically matrix for the applied transformations
// aScale was in most cases built from a rectangle including edge
// and is therefore mathematically too large by 1
if (aScale.getX() > 2.0 && aScale.getY() > 2.0)
aScale -= basegfx::B2DTuple(1.0, 1.0);
basegfx::B2DHomMatrix aMathMat = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
aScale, -fShearX, fRotate,
aTranslate);
// Use matrix to get current center
basegfx::B2DPoint aCenter(0.5,0.5);
aCenter = aMathMat * aCenter;
double fCenterX = aCenter.getX();
double fCenterY = aCenter.getY();
if (bIsMirroredX) // vertical axis
NbcMirror(Point(basegfx::fround<tools::Long>(fCenterX), basegfx::fround<tools::Long>(fCenterY)),
Point(basegfx::fround<tools::Long>(fCenterX), basegfx::fround<tools::Long>(fCenterY + 1000.0)));
if (bIsMirroredY) // horizontal axis
NbcMirror(Point(basegfx::fround<tools::Long>(fCenterX), basegfx::fround<tools::Long>(fCenterY)),
Point(basegfx::fround<tools::Long>(fCenterX + 1000.0), basegfx::fround<tools::Long>(fCenterY)));
BroadcastObjectChange();
SendUserCall(SdrUserCallType::Resize, aBoundRect0);
}
// taking fObjectRotation instead of aGeo.nAngle
bool SdrObjCustomShape::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
{
// get turn and shear
double fRotate = basegfx::deg2rad(m_fObjectRotation);
double fShearX = toRadians(maGeo.m_nShearAngle);
// get aRectangle, this is the unrotated snaprect
tools::Rectangle aRectangle(getRectangle());
bool bMirroredX = IsMirroredX();
bool bMirroredY = IsMirroredY();
if ( bMirroredX || bMirroredY )
{ // we have to retrieve the unmirrored rect
GeoStat aNewGeo(maGeo);
if ( bMirroredX )
{
fShearX = -fShearX;
tools::Polygon aPol = Rect2Poly(getRectangle(), aNewGeo);
tools::Rectangle aBoundRect( aPol.GetBoundRect() );
Point aRef1( ( aBoundRect.Left() + aBoundRect.Right() ) >> 1, aBoundRect.Top() );
Point aRef2( aRef1.X(), aRef1.Y() + 1000 );
sal_uInt16 i;
sal_uInt16 nPointCount=aPol.GetSize();
for (i=0; i<nPointCount; i++)
{
MirrorPoint(aPol[i],aRef1,aRef2);
}
// mirror polygon and move it a bit
tools::Polygon aPol0(aPol);
aPol[0]=aPol0[1];
aPol[1]=aPol0[0];
aPol[2]=aPol0[3];
aPol[3]=aPol0[2];
aPol[4]=aPol0[1];
aRectangle = svx::polygonToRectangle(aPol, aNewGeo);
}
if ( bMirroredY )
{
fShearX = -fShearX;
tools::Polygon aPol( Rect2Poly( aRectangle, aNewGeo ) );
tools::Rectangle aBoundRect( aPol.GetBoundRect() );
Point aRef1( aBoundRect.Left(), ( aBoundRect.Top() + aBoundRect.Bottom() ) >> 1 );
Point aRef2( aRef1.X() + 1000, aRef1.Y() );
sal_uInt16 i;
sal_uInt16 nPointCount=aPol.GetSize();
for (i=0; i<nPointCount; i++)
{
MirrorPoint(aPol[i],aRef1,aRef2);
}
// mirror polygon and move it a bit
tools::Polygon aPol0(aPol);
aPol[0]=aPol0[1]; // This was WRONG for vertical (!)
aPol[1]=aPol0[0]; // #i121932# Despite my own comment above
aPol[2]=aPol0[3]; // it was *not* wrong even when the reordering
aPol[3]=aPol0[2]; // *seems* to be specific for X-Mirrorings. Oh
aPol[4]=aPol0[1]; // will I be happy when this old stuff is |gone| with aw080 (!)
aRectangle = svx::polygonToRectangle(aPol, aNewGeo);
}
}
// fill other values
basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
// position may be relative to anchorpos, convert
if(getSdrModelFromSdrObject().IsWriter())
{
if(GetAnchorPos().X() || GetAnchorPos().Y())
{
aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
}
}
// build matrix
rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
aScale,
basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
-fRotate,
aTranslate);
return false;
}
std::unique_ptr<sdr::contact::ViewContact> SdrObjCustomShape::CreateObjectSpecificViewContact()
{
return std::make_unique<sdr::contact::ViewContactOfSdrObjCustomShape>(*this);
}
// #i33136#
bool SdrObjCustomShape::doConstructOrthogonal(std::u16string_view rName)
{
bool bRetval(false);
if(o3tl::equalsIgnoreAsciiCase(rName, u"quadrat"))
{
bRetval = true;
}
else if(o3tl::equalsIgnoreAsciiCase(rName, u"round-quadrat"))
{
bRetval = true;
}
else if(o3tl::equalsIgnoreAsciiCase(rName, u"circle"))
{
bRetval = true;
}
else if(o3tl::equalsIgnoreAsciiCase(rName, u"circle-pie"))
{
bRetval = true;
}
else if(o3tl::equalsIgnoreAsciiCase(rName, u"ring"))
{
bRetval = true;
}
return bRetval;
}
// #i37011# centralize throw-away of render geometry
void SdrObjCustomShape::InvalidateRenderGeometry()
{
mXRenderedCustomShape = nullptr;
mpLastShadowGeometry = nullptr;
}
void SdrObjCustomShape::setUnoShape(const uno::Reference<drawing::XShape>& rxUnoShape)
{
SdrTextObj::setUnoShape(rxUnoShape);
// The shape engine is created with _current_ shape. This means we
// _must_ reset it when the shape changes.
mxCustomShapeEngine.clear();
}
OUString SdrObjCustomShape::GetCustomShapeName() const
{
OUString sShapeName;
OUString aEngine( GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
if ( aEngine.isEmpty()
|| aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
{
OUString sShapeType;
const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( u"Type"_ustr );
if ( pAny && ( *pAny >>= sShapeType ) )
sShapeName = EnhancedCustomShapeTypeNames::GetAccName( sShapeType );
}
return sShapeName;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */