3275 lines
125 KiB
C++
3275 lines
125 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 <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 <o3tl/string_view.hxx>
|
|
#include "presetooxhandleadjustmentrelations.hxx"
|
|
#include <editeng/frmdiritem.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());
|
|
BitmapEx aBitmapEx(aGraphicObject.GetGraphic().GetBitmapEx());
|
|
|
|
if(!aBitmapEx.IsEmpty())
|
|
{
|
|
ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
|
|
pVirDev->SetOutputSizePixel(aBitmapEx.GetSizePixel());
|
|
BitmapFilter::Filter(aBitmapEx, BitmapShadowFilter(aShadowColor));
|
|
pVirDev->DrawBitmapEx(Point(), aBitmapEx);
|
|
aGraphicObject.SetGraphic(Graphic(pVirDev->GetBitmapEx(Point(0,0), aBitmapEx.GetSizePixel())));
|
|
}
|
|
|
|
aTempSet.Put(XFillBitmapItem(aGraphicObject));
|
|
aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
|
|
}
|
|
|
|
// set attributes and paint shadow object
|
|
pRetval->SetMergedItemSet( aTempSet );
|
|
}
|
|
return pRetval;
|
|
}
|
|
|
|
|
|
uno::Reference<drawing::XCustomShapeEngine> const & SdrObjCustomShape::GetCustomShapeEngine() const
|
|
{
|
|
if (mxCustomShapeEngine.is())
|
|
return mxCustomShapeEngine;
|
|
|
|
uno::Reference<drawing::XShape> aXShape = GetXShapeForSdrObject(const_cast<SdrObjCustomShape*>(this));
|
|
if ( !aXShape )
|
|
return mxCustomShapeEngine;
|
|
|
|
const uno::Reference<uno::XComponentContext>& xContext( ::comphelper::getProcessComponentContext() );
|
|
|
|
OUString aEngine(GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue());
|
|
static constexpr OUStringLiteral sEnhancedCustomShapeEngine = u"com.sun.star.drawing.EnhancedCustomShapeEngine";
|
|
if ( aEngine.isEmpty() )
|
|
aEngine = sEnhancedCustomShapeEngine;
|
|
|
|
{
|
|
static constexpr OUString sCustomShape = u"CustomShape"_ustr;
|
|
uno::Sequence<beans::PropertyValue> aPropValues{ comphelper::makePropertyValue(sCustomShape,
|
|
aXShape) };
|
|
uno::Sequence<uno::Any> aArgument{ uno::Any(aPropValues) };
|
|
try
|
|
{
|
|
uno::Reference<uno::XInterface> xInterface(xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aEngine, aArgument, xContext));
|
|
if (xInterface.is())
|
|
mxCustomShapeEngine.set(xInterface, uno::UNO_QUERY);
|
|
}
|
|
catch (const loader::CannotActivateFactoryException&)
|
|
{
|
|
}
|
|
}
|
|
|
|
return mxCustomShapeEngine;
|
|
}
|
|
|
|
const SdrObject* SdrObjCustomShape::GetSdrObjectFromCustomShape() const
|
|
{
|
|
if ( !mXRenderedCustomShape.is() )
|
|
{
|
|
uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
|
|
if ( xCustomShapeEngine.is() )
|
|
const_cast<SdrObjCustomShape*>(this)->mXRenderedCustomShape = xCustomShapeEngine->render();
|
|
}
|
|
SdrObject* pRenderedCustomShape = mXRenderedCustomShape.is()
|
|
? SdrObject::getSdrObjectFromXShape( mXRenderedCustomShape )
|
|
: nullptr;
|
|
return pRenderedCustomShape;
|
|
}
|
|
|
|
// #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;
|
|
|
|
uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
|
|
if ( xCustomShapeEngine.is() )
|
|
{
|
|
awt::Rectangle aR( xCustomShapeEngine->getTextBounds() );
|
|
if ( aR.Width > 1 && aR.Height > 1 )
|
|
{
|
|
rTextBound = tools::Rectangle( Point( aR.X, aR.Y ), Size( aR.Width, aR.Height ) );
|
|
bRet = true;
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
basegfx::B2DPolyPolygon SdrObjCustomShape::GetLineGeometry( const bool bBezierAllowed ) const
|
|
{
|
|
basegfx::B2DPolyPolygon aRetval;
|
|
uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
|
|
if ( xCustomShapeEngine.is() )
|
|
{
|
|
drawing::PolyPolygonBezierCoords aBezierCoords = xCustomShapeEngine->getLineGeometry();
|
|
try
|
|
{
|
|
aRetval = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon( aBezierCoords );
|
|
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
|
|
{
|
|
uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
|
|
if ( xCustomShapeEngine.is() )
|
|
{
|
|
int i;
|
|
uno::Sequence<uno::Reference<drawing::XCustomShapeHandle>> xInteractionHandles( xCustomShapeEngine->getInteraction() );
|
|
for ( i = 0; i < xInteractionHandles.getLength(); i++ )
|
|
{
|
|
if ( xInteractionHandles[ i ].is() )
|
|
{
|
|
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 );
|
|
|
|
const sal_Int32* pDefData = nullptr;
|
|
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 ) // 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++;
|
|
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->nElements && pDefCustomShape->pElements )
|
|
{
|
|
sal_Int32 i, nCount = pDefCustomShape->nElements;
|
|
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->nElements && pDefCustomShape->pElements )
|
|
{
|
|
sal_Int32 i, nCount = pDefCustomShape->nElements;
|
|
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->nElements == 0 ) || ( pDefCustomShape->pElements == nullptr ) ) )
|
|
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;
|
|
|
|
const SdrObject* pRenderedCustomShape = SdrObject::getSdrObjectFromXShape( mXRenderedCustomShape );
|
|
if ( !pRenderedCustomShape )
|
|
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(*pRenderedCustomShape);
|
|
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;
|
|
|
|
SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
|
|
SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH> // contains SDRATTR_TEXT_MAXFRAMEWIDTH
|
|
aSet(*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() )
|
|
{
|
|
SdrObject* pRenderedCustomShape = SdrObject::getSdrObjectFromXShape(mXRenderedCustomShape);
|
|
if ( pRenderedCustomShape )
|
|
{
|
|
// #i97149# the visualisation shape needs to be informed
|
|
// about change, too
|
|
pRenderedCustomShape->ActionChanged();
|
|
pRenderedCustomShape->NbcMove( rSiz );
|
|
}
|
|
}
|
|
|
|
// #i37011# adapt geometry shadow
|
|
if(mpLastShadowGeometry)
|
|
{
|
|
mpLastShadowGeometry->NbcMove( rSiz );
|
|
}
|
|
}
|
|
|
|
void SdrObjCustomShape::NbcResize( const Point& rRef, const Fraction& rxFact, const Fraction& 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.GetNumerator() != rxFact.GetDenominator() )
|
|
|| ( ryFact.GetNumerator()!= ryFact.GetDenominator() ) )
|
|
{
|
|
if ( ( ( rxFact.GetNumerator() < 0 ) && ( rxFact.GetDenominator() > 0 ) ) ||
|
|
( ( rxFact.GetNumerator() > 0 ) && ( rxFact.GetDenominator() < 0 ) ) )
|
|
{
|
|
SetMirroredX( !IsMirroredX() );
|
|
}
|
|
if ( ( ( ryFact.GetNumerator() < 0 ) && ( ryFact.GetDenominator() > 0 ) ) ||
|
|
( ( ryFact.GetNumerator() > 0 ) && ( ryFact.GetDenominator() < 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
|
|
SfxItemSetFixed<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> aNewSet(*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);
|
|
rOutliner.SetUpdateLayout(true);
|
|
// TODO: add the optimization with bPortionInfoChecked again.
|
|
OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
|
|
if( pOutlinerParaObject != nullptr )
|
|
{
|
|
rOutliner.SetText(*pOutlinerParaObject);
|
|
rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
rOutliner.Clear();
|
|
}
|
|
}
|
|
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 )
|
|
{
|
|
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::CloneSdrObject(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;
|
|
SdrObject* pRenderedCustomShape = nullptr;
|
|
|
|
if ( !mXRenderedCustomShape.is() )
|
|
{
|
|
// force CustomShape
|
|
GetSdrObjectFromCustomShape();
|
|
}
|
|
|
|
if ( mXRenderedCustomShape.is() )
|
|
{
|
|
pRenderedCustomShape = SdrObject::getSdrObjectFromXShape(mXRenderedCustomShape);
|
|
}
|
|
|
|
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,
|
|
basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
|
|
basegfx::fTools::equalZero(fRotate) ? 0.0 : 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,
|
|
basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
|
|
basegfx::fTools::equalZero(fRotate) ? 0.0 : 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*/)
|
|
{
|
|
// 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
|
|
{
|
|
Mirror(aCurrentCenter, Point(aCurrentCenter.X(), aCurrentCenter.Y() + 1000));
|
|
}
|
|
if (bIsMirroredY) // mirror on the x-axis
|
|
{
|
|
Mirror(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);
|
|
SetLogicRect(aBaseRect);
|
|
|
|
// Apply flipping from Matrix, which is a transformation relative to origin
|
|
if (aScale.getX() < 0.0)
|
|
Mirror(Point(0, 0), Point(0, 1000)); // mirror on the y-axis
|
|
if (aScale.getY() < 0.0)
|
|
Mirror(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();
|
|
Shear(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();
|
|
Rotate(Point(), aGeoStat.m_nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
|
|
}
|
|
|
|
// translate?
|
|
if(!aTranslate.equalZero())
|
|
{
|
|
Move(Size(basegfx::fround<tools::Long>(aTranslate.getX()),
|
|
basegfx::fround<tools::Long>(aTranslate.getY())));
|
|
}
|
|
|
|
// 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, basegfx::fTools::equalZero(fRotate) ? 0.0 : 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
|
|
Mirror(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
|
|
Mirror(Point(basegfx::fround<tools::Long>(fCenterX), basegfx::fround<tools::Long>(fCenterY)),
|
|
Point(basegfx::fround<tools::Long>(fCenterX + 1000.0), basegfx::fround<tools::Long>(fCenterY)));
|
|
}
|
|
|
|
// 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),
|
|
basegfx::fTools::equalZero(fRotate) ? 0.0 : -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: */
|