nss: upgrade to release 3.73
[LibreOffice.git] / svx / source / svdraw / svdoashp.cxx
blob30552b63fa4d9c71c7786124798fc9f14db88c7f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svx/svdoashp.hxx>
21 #include <svx/unoapi.hxx>
22 #include <com/sun/star/loader/CannotActivateFactoryException.hpp>
23 #include <com/sun/star/drawing/XShape.hpp>
24 #include <com/sun/star/drawing/XCustomShapeEngine.hpp>
25 #include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
26 #include <com/sun/star/beans/PropertyValue.hpp>
27 #include <com/sun/star/awt/Rectangle.hpp>
28 #include <com/sun/star/uno/XComponentContext.hpp>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/sequenceashashmap.hxx>
31 #include <com/sun/star/uno/Sequence.h>
32 #include <tools/helpers.hxx>
33 #include <svx/svddrag.hxx>
34 #include <svx/svddrgmt.hxx>
35 #include <svx/svdmodel.hxx>
36 #include <svx/svdpage.hxx>
37 #include <svx/svditer.hxx>
38 #include <svx/svdobj.hxx>
39 #include <svx/svdtrans.hxx>
40 #include <svx/dialmgr.hxx>
41 #include <svx/strings.hrc>
42 #include <editeng/eeitem.hxx>
43 #include <editeng/editstat.hxx>
44 #include <editeng/adjustitem.hxx>
45 #include <svx/svdoutl.hxx>
46 #include <editeng/outlobj.hxx>
47 #include <svx/sdtfchim.hxx>
48 #include <svx/EnhancedCustomShapeGeometry.hxx>
49 #include <svx/EnhancedCustomShapeTypeNames.hxx>
50 #include <svx/EnhancedCustomShape2d.hxx>
51 #include <com/sun/star/beans/PropertyValues.hpp>
52 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
53 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
54 #include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
55 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
56 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
57 #include <editeng/writingmodeitem.hxx>
58 #include <svx/xlineit0.hxx>
59 #include <svx/xlnclit.hxx>
60 #include <sdr/properties/customshapeproperties.hxx>
61 #include <sdr/contact/viewcontactofsdrobjcustomshape.hxx>
62 #include <svx/xlntrit.hxx>
63 #include <svx/xfillit0.hxx>
64 #include <svx/xfltrit.hxx>
65 #include <svx/xflclit.hxx>
66 #include <svx/xflgrit.hxx>
67 #include <svx/xflhtit.hxx>
68 #include <svx/xbtmpit.hxx>
69 #include <vcl/virdev.hxx>
70 #include <svx/svdview.hxx>
71 #include <svx/sdmetitm.hxx>
72 #include <svx/sdprcitm.hxx>
73 #include <svx/sdshitm.hxx>
74 #include <svx/sdsxyitm.hxx>
75 #include <svx/sdtmfitm.hxx>
76 #include <svx/sdasitm.hxx>
77 #include <basegfx/polygon/b2dpolypolygontools.hxx>
78 #include <basegfx/matrix/b2dhommatrix.hxx>
79 #include <basegfx/matrix/b2dhommatrixtools.hxx>
80 #include <basegfx/polygon/b2dpolygon.hxx>
81 #include <basegfx/polygon/b2dpolygontools.hxx>
82 #include <basegfx/range/b2drange.hxx>
83 #include <svdobjplusdata.hxx>
84 #include <rtl/ustrbuf.hxx>
85 #include <sal/log.hxx>
86 #include "presetooxhandleadjustmentrelations.hxx"
88 using namespace ::com::sun::star;
89 using namespace ::com::sun::star::uno;
90 using namespace ::com::sun::star::lang;
91 using namespace ::com::sun::star::beans;
92 using namespace ::com::sun::star::drawing;
94 static void lcl_ShapeSegmentFromBinary( EnhancedCustomShapeSegment& rSegInfo, sal_uInt16 nSDat )
96 switch( nSDat >> 8 )
98 case 0x00 :
99 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::LINETO;
100 rSegInfo.Count = nSDat & 0xff;
101 if ( !rSegInfo.Count )
102 rSegInfo.Count = 1;
103 break;
104 case 0x20 :
105 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CURVETO;
106 rSegInfo.Count = nSDat & 0xff;
107 if ( !rSegInfo.Count )
108 rSegInfo.Count = 1;
109 break;
110 case 0x40 :
111 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::MOVETO;
112 rSegInfo.Count = nSDat & 0xff;
113 if ( !rSegInfo.Count )
114 rSegInfo.Count = 1;
115 break;
116 case 0x60 :
117 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
118 rSegInfo.Count = 0;
119 break;
120 case 0x80 :
121 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
122 rSegInfo.Count = 0;
123 break;
124 case 0xa1 :
125 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO;
126 rSegInfo.Count = ( nSDat & 0xff ) / 3;
127 break;
128 case 0xa2 :
129 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE;
130 rSegInfo.Count = ( nSDat & 0xff ) / 3;
131 break;
132 case 0xa3 :
133 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ARCTO;
134 rSegInfo.Count = ( nSDat & 0xff ) >> 2;
135 break;
136 case 0xa4 :
137 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ARC;
138 rSegInfo.Count = ( nSDat & 0xff ) >> 2;
139 break;
140 case 0xa5 :
141 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO;
142 rSegInfo.Count = ( nSDat & 0xff ) >> 2;
143 break;
144 case 0xa6 :
145 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CLOCKWISEARC;
146 rSegInfo.Count = ( nSDat & 0xff ) >> 2;
147 break;
148 case 0xa7 :
149 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX;
150 rSegInfo.Count = nSDat & 0xff;
151 break;
152 case 0xa8 :
153 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY;
154 rSegInfo.Count = nSDat & 0xff;
155 break;
156 case 0xaa :
157 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::NOFILL;
158 rSegInfo.Count = 0;
159 break;
160 case 0xab :
161 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::NOSTROKE;
162 rSegInfo.Count = 0;
163 break;
164 default:
165 case 0xf8 :
166 rSegInfo.Command = EnhancedCustomShapeSegmentCommand::UNKNOWN;
167 rSegInfo.Count = nSDat;
168 break;
172 static MSO_SPT ImpGetCustomShapeType( const SdrObjCustomShape& rCustoShape )
174 MSO_SPT eRetValue = mso_sptNil;
176 OUString aEngine( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
177 if ( aEngine.isEmpty() || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
179 OUString sShapeType;
180 const SdrCustomShapeGeometryItem& rGeometryItem( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
181 const Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
182 if ( pAny && ( *pAny >>= sShapeType ) )
183 eRetValue = EnhancedCustomShapeTypeNames::Get( sShapeType );
185 return eRetValue;
188 static bool ImpVerticalSwitch( const SdrObjCustomShape& rCustoShape )
190 bool bRet = false;
191 MSO_SPT eShapeType( ImpGetCustomShapeType( rCustoShape ) );
192 switch( eShapeType )
194 case mso_sptAccentBorderCallout90 : // 2 ortho
195 case mso_sptBorderCallout1 : // 2 diag
196 case mso_sptBorderCallout2 : // 3
198 bRet = true;
200 break;
201 default: break;
203 return bRet;
206 // #i37011# create a clone with all attributes changed to shadow attributes
207 // and translation executed, too.
208 static SdrObject* ImpCreateShadowObjectClone(const SdrObject& rOriginal, const SfxItemSet& rOriginalSet)
210 SdrObject* pRetval = nullptr;
211 const bool bShadow(rOriginalSet.Get(SDRATTR_SHADOW).GetValue());
213 if(bShadow)
215 // create a shadow representing object
216 const sal_Int32 nXDist(rOriginalSet.Get(SDRATTR_SHADOWXDIST).GetValue());
217 const sal_Int32 nYDist(rOriginalSet.Get(SDRATTR_SHADOWYDIST).GetValue());
218 const ::Color aShadowColor(rOriginalSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue());
219 const sal_uInt16 nShadowTransparence(rOriginalSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue());
220 pRetval = rOriginal.CloneSdrObject(rOriginal.getSdrModelFromSdrObject());
221 DBG_ASSERT(pRetval, "ImpCreateShadowObjectClone: Could not clone object (!)");
223 // look for used stuff
224 SdrObjListIter aIterator(rOriginal);
225 bool bLineUsed(false);
226 bool bAllFillUsed(false);
227 bool bSolidFillUsed(false);
228 bool bGradientFillUsed(false);
229 bool bHatchFillUsed(false);
230 bool bBitmapFillUsed(false);
232 while(aIterator.IsMore())
234 SdrObject* pObj = aIterator.Next();
235 drawing::FillStyle eFillStyle = pObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
237 if(!bLineUsed)
239 drawing::LineStyle eLineStyle = pObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
241 if(drawing::LineStyle_NONE != eLineStyle)
243 bLineUsed = true;
247 if(!bAllFillUsed)
249 if(!bSolidFillUsed && drawing::FillStyle_SOLID == eFillStyle)
251 bSolidFillUsed = true;
252 bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
254 if(!bGradientFillUsed && drawing::FillStyle_GRADIENT == eFillStyle)
256 bGradientFillUsed = true;
257 bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
259 if(!bHatchFillUsed && drawing::FillStyle_HATCH == eFillStyle)
261 bHatchFillUsed = true;
262 bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
264 if(!bBitmapFillUsed && drawing::FillStyle_BITMAP == eFillStyle)
266 bBitmapFillUsed = true;
267 bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
272 // translate to shadow coordinates
273 pRetval->NbcMove(Size(nXDist, nYDist));
275 // set items as needed
276 SfxItemSet aTempSet(rOriginalSet);
278 // if a SvxWritingModeItem (Top->Bottom) is set the text object
279 // is creating a paraobject, but paraobjects can not be created without model. So
280 // we are preventing the crash by setting the writing mode always left to right,
281 // this is not bad since our shadow geometry does not contain text.
282 aTempSet.Put( SvxWritingModeItem( css::text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION ) );
284 // no shadow
285 aTempSet.Put(makeSdrShadowItem(false));
286 aTempSet.Put(makeSdrShadowXDistItem(0));
287 aTempSet.Put(makeSdrShadowYDistItem(0));
289 // line color and transparency like shadow
290 if(bLineUsed)
292 aTempSet.Put(XLineColorItem(OUString(), aShadowColor));
293 aTempSet.Put(XLineTransparenceItem(nShadowTransparence));
296 // fill color and transparency like shadow
297 if(bSolidFillUsed)
299 aTempSet.Put(XFillColorItem(OUString(), aShadowColor));
300 aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
303 // gradient and transparency like shadow
304 if(bGradientFillUsed)
306 XGradient aGradient(rOriginalSet.Get(XATTR_FILLGRADIENT).GetGradientValue());
307 sal_uInt8 nStartLuminance(aGradient.GetStartColor().GetLuminance());
308 sal_uInt8 nEndLuminance(aGradient.GetEndColor().GetLuminance());
310 if(aGradient.GetStartIntens() != 100)
312 nStartLuminance = static_cast<sal_uInt8>(nStartLuminance * (static_cast<double>(aGradient.GetStartIntens()) / 100.0));
315 if(aGradient.GetEndIntens() != 100)
317 nEndLuminance = static_cast<sal_uInt8>(nEndLuminance * (static_cast<double>(aGradient.GetEndIntens()) / 100.0));
320 ::Color aStartColor(
321 static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetRed()) / 256),
322 static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetGreen()) / 256),
323 static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetBlue()) / 256));
325 ::Color aEndColor(
326 static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetRed()) / 256),
327 static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetGreen()) / 256),
328 static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetBlue()) / 256));
330 aGradient.SetStartColor(aStartColor);
331 aGradient.SetEndColor(aEndColor);
332 aTempSet.Put(XFillGradientItem(aGradient));
333 aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
336 // hatch and transparency like shadow
337 if(bHatchFillUsed)
339 XHatch aHatch(rOriginalSet.Get(XATTR_FILLHATCH).GetHatchValue());
340 aHatch.SetColor(aShadowColor);
341 aTempSet.Put(XFillHatchItem(aHatch));
342 aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
345 // bitmap and transparency like shadow
346 if(bBitmapFillUsed)
348 GraphicObject aGraphicObject(rOriginalSet.Get(XATTR_FILLBITMAP).GetGraphicObject());
349 const BitmapEx aBitmapEx(aGraphicObject.GetGraphic().GetBitmapEx());
351 if(!aBitmapEx.IsEmpty())
353 ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
354 pVirDev->SetOutputSizePixel(aBitmapEx.GetSizePixel());
355 pVirDev->DrawShadowBitmapEx(aBitmapEx, aShadowColor);
356 aGraphicObject.SetGraphic(Graphic(pVirDev->GetBitmapEx(Point(0,0), aBitmapEx.GetSizePixel())));
359 aTempSet.Put(XFillBitmapItem(aGraphicObject));
360 aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
363 // set attributes and paint shadow object
364 pRetval->SetMergedItemSet( aTempSet );
366 return pRetval;
370 Reference< XCustomShapeEngine > const & SdrObjCustomShape::GetCustomShapeEngine() const
372 if (mxCustomShapeEngine.is())
373 return mxCustomShapeEngine;
375 OUString aEngine(GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue());
376 if ( aEngine.isEmpty() )
377 aEngine = "com.sun.star.drawing.EnhancedCustomShapeEngine";
379 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
381 Reference< XShape > aXShape = GetXShapeForSdrObject(const_cast<SdrObjCustomShape*>(this));
382 if ( aXShape.is() )
384 Sequence< Any > aArgument( 1 );
385 Sequence< PropertyValue > aPropValues( 1 );
386 aPropValues[ 0 ].Name = "CustomShape";
387 aPropValues[ 0 ].Value <<= aXShape;
388 aArgument[ 0 ] <<= aPropValues;
391 Reference<XInterface> xInterface(xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aEngine, aArgument, xContext));
392 if (xInterface.is())
393 mxCustomShapeEngine.set( xInterface, UNO_QUERY );
395 catch (const css::loader::CannotActivateFactoryException&)
400 return mxCustomShapeEngine;
403 const SdrObject* SdrObjCustomShape::GetSdrObjectFromCustomShape() const
405 if ( !mXRenderedCustomShape.is() )
407 Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
408 if ( xCustomShapeEngine.is() )
409 const_cast<SdrObjCustomShape*>(this)->mXRenderedCustomShape = xCustomShapeEngine->render();
411 SdrObject* pRenderedCustomShape = mXRenderedCustomShape.is()
412 ? GetSdrObjectFromXShape( mXRenderedCustomShape )
413 : nullptr;
414 return pRenderedCustomShape;
417 // #i37011# Shadow geometry creation
418 const SdrObject* SdrObjCustomShape::GetSdrObjectShadowFromCustomShape() const
420 if(!mpLastShadowGeometry)
422 const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
423 if(pSdrObject)
425 const SfxItemSet& rOriginalSet = GetObjectItemSet();
426 const bool bShadow(rOriginalSet.Get( SDRATTR_SHADOW ).GetValue());
428 if(bShadow)
430 // create a clone with all attributes changed to shadow attributes
431 // and translation executed, too.
432 const_cast<SdrObjCustomShape*>(this)->mpLastShadowGeometry =
433 ImpCreateShadowObjectClone(*pSdrObject, rOriginalSet);
438 return mpLastShadowGeometry;
441 bool SdrObjCustomShape::IsTextPath() const
443 const OUString sTextPath( "TextPath" );
444 bool bTextPathOn = false;
445 const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
446 const Any* pAny = rGeometryItem.GetPropertyValueByName( sTextPath, sTextPath );
447 if ( pAny )
448 *pAny >>= bTextPathOn;
449 return bTextPathOn;
452 bool SdrObjCustomShape::UseNoFillStyle() const
454 bool bRet = false;
455 OUString sShapeType;
456 const OUString sType( "Type" );
457 const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
458 const Any* pAny = rGeometryItem.GetPropertyValueByName( sType );
459 if ( pAny )
460 *pAny >>= sShapeType;
461 bRet = !IsCustomShapeFilledByDefault( EnhancedCustomShapeTypeNames::Get( sType ) );
463 return bRet;
466 bool SdrObjCustomShape::IsMirroredX() const
468 bool bMirroredX = false;
469 SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
470 css::uno::Any* pAny = aGeometryItem.GetPropertyValueByName( "MirroredX" );
471 if ( pAny )
472 *pAny >>= bMirroredX;
473 return bMirroredX;
475 bool SdrObjCustomShape::IsMirroredY() const
477 bool bMirroredY = false;
478 SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
479 css::uno::Any* pAny = aGeometryItem.GetPropertyValueByName( "MirroredY" );
480 if ( pAny )
481 *pAny >>= bMirroredY;
482 return bMirroredY;
484 void SdrObjCustomShape::SetMirroredX( const bool bMirrorX )
486 SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
487 PropertyValue aPropVal;
488 aPropVal.Name = "MirroredX";
489 aPropVal.Value <<= bMirrorX;
490 aGeometryItem.SetPropertyValue( aPropVal );
491 SetMergedItem( aGeometryItem );
493 void SdrObjCustomShape::SetMirroredY( const bool bMirrorY )
495 SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
496 PropertyValue aPropVal;
497 aPropVal.Name = "MirroredY";
498 aPropVal.Value <<= bMirrorY;
499 aGeometryItem.SetPropertyValue( aPropVal );
500 SetMergedItem( aGeometryItem );
503 double SdrObjCustomShape::GetExtraTextRotation( const bool bPreRotation ) const
505 const css::uno::Any* pAny;
506 const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
507 pAny = rGeometryItem.GetPropertyValueByName( bPreRotation ? OUString( "TextPreRotateAngle" ) : OUString( "TextRotateAngle" ) );
508 double fExtraTextRotateAngle = 0.0;
509 if ( pAny )
510 *pAny >>= fExtraTextRotateAngle;
511 return fExtraTextRotateAngle;
514 bool SdrObjCustomShape::GetTextBounds( tools::Rectangle& rTextBound ) const
516 bool bRet = false;
518 Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
519 if ( xCustomShapeEngine.is() )
521 awt::Rectangle aR( xCustomShapeEngine->getTextBounds() );
522 if ( aR.Width > 1 && aR.Height > 1 )
524 rTextBound = tools::Rectangle( Point( aR.X, aR.Y ), Size( aR.Width, aR.Height ) );
525 bRet = true;
528 return bRet;
530 basegfx::B2DPolyPolygon SdrObjCustomShape::GetLineGeometry( const bool bBezierAllowed ) const
532 basegfx::B2DPolyPolygon aRetval;
533 Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
534 if ( xCustomShapeEngine.is() )
536 css::drawing::PolyPolygonBezierCoords aBezierCoords = xCustomShapeEngine->getLineGeometry();
539 aRetval = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon( aBezierCoords );
540 if ( !bBezierAllowed && aRetval.areControlPointsUsed())
542 aRetval = basegfx::utils::adaptiveSubdivideByAngle(aRetval);
545 catch ( const css::lang::IllegalArgumentException & )
549 return aRetval;
552 std::vector< SdrCustomShapeInteraction > SdrObjCustomShape::GetInteractionHandles() const
554 std::vector< SdrCustomShapeInteraction > aRet;
557 Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
558 if ( xCustomShapeEngine.is() )
560 int i;
561 Sequence< Reference< XCustomShapeHandle > > xInteractionHandles( xCustomShapeEngine->getInteraction() );
562 for ( i = 0; i < xInteractionHandles.getLength(); i++ )
564 if ( xInteractionHandles[ i ].is() )
566 SdrCustomShapeInteraction aSdrCustomShapeInteraction;
567 aSdrCustomShapeInteraction.xInteraction = xInteractionHandles[ i ];
568 aSdrCustomShapeInteraction.aPosition = xInteractionHandles[ i ]->getPosition();
570 CustomShapeHandleModes nMode = CustomShapeHandleModes::NONE;
571 switch( ImpGetCustomShapeType( *this ) )
573 case mso_sptAccentBorderCallout90 : // 2 ortho
575 if (i == 0)
576 nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
577 else if (i == 1)
578 nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE | CustomShapeHandleModes::ORTHO4;
580 break;
582 case mso_sptChevron :
583 case mso_sptHomePlate :
584 nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX;
585 break;
587 case mso_sptWedgeRectCallout :
588 case mso_sptWedgeRRectCallout :
589 case mso_sptCloudCallout :
590 case mso_sptWedgeEllipseCallout :
592 if (i == 0)
593 nMode |= CustomShapeHandleModes::RESIZE_FIXED;
595 break;
597 case mso_sptBorderCallout1 : // 2 diag
599 if (i == 0)
600 nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
601 else if (i == 1)
602 nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
604 break;
605 case mso_sptBorderCallout2 : // 3
607 if (i == 0)
608 nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
609 else if (i == 2)
610 nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
612 break;
613 case mso_sptCallout90 :
614 case mso_sptAccentCallout90 :
615 case mso_sptBorderCallout90 :
616 case mso_sptCallout1 :
617 case mso_sptCallout2 :
618 case mso_sptCallout3 :
619 case mso_sptAccentCallout1 :
620 case mso_sptAccentCallout2 :
621 case mso_sptAccentCallout3 :
622 case mso_sptBorderCallout3 :
623 case mso_sptAccentBorderCallout1 :
624 case mso_sptAccentBorderCallout2 :
625 case mso_sptAccentBorderCallout3 :
627 if (i == 0)
628 nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
630 break;
631 default: break;
633 aSdrCustomShapeInteraction.nMode = nMode;
634 aRet.push_back( aSdrCustomShapeInteraction );
639 catch( const uno::RuntimeException& )
642 return aRet;
646 // BaseProperties section
647 #define DEFAULT_MINIMUM_SIGNED_COMPARE (sal_Int32(0x80000000))
648 #define DEFAULT_MAXIMUM_SIGNED_COMPARE (sal_Int32(0x7fffffff))
650 static sal_Int32 GetNumberOfProperties ( const SvxMSDffHandle* pData )
652 sal_Int32 nPropertiesNeeded=1; // position is always needed
653 SvxMSDffHandleFlags nFlags = pData->nFlags;
655 if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
656 nPropertiesNeeded++;
657 if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
658 nPropertiesNeeded++;
659 if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
660 nPropertiesNeeded++;
661 if ( nFlags & SvxMSDffHandleFlags::POLAR )
663 nPropertiesNeeded++;
664 if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
666 if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
667 nPropertiesNeeded++;
668 if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
669 nPropertiesNeeded++;
672 else if ( nFlags & SvxMSDffHandleFlags::RANGE )
674 if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
675 nPropertiesNeeded++;
676 if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
677 nPropertiesNeeded++;
678 if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
679 nPropertiesNeeded++;
680 if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
681 nPropertiesNeeded++;
684 return nPropertiesNeeded;
687 static void lcl_ShapePropertiesFromDFF( const SvxMSDffHandle* pData, css::beans::PropertyValues& rPropValues )
689 SvxMSDffHandleFlags nFlags = pData->nFlags;
690 sal_Int32 n=0;
692 // POSITION
694 css::drawing::EnhancedCustomShapeParameterPair aPosition;
695 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.First, pData->nPositionX, true, true );
696 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.Second, pData->nPositionY, true, false );
697 rPropValues[ n ].Name = "Position";
698 rPropValues[ n++ ].Value <<= aPosition;
700 if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
702 rPropValues[ n ].Name = "MirroredX";
703 rPropValues[ n++ ].Value <<= true;
705 if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
707 rPropValues[ n ].Name = "MirroredY";
708 rPropValues[ n++ ].Value <<= true;
710 if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
712 rPropValues[ n ].Name = "Switched";
713 rPropValues[ n++ ].Value <<= true;
715 if ( nFlags & SvxMSDffHandleFlags::POLAR )
717 css::drawing::EnhancedCustomShapeParameterPair aCenter;
718 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.First, pData->nCenterX,
719 bool( nFlags & SvxMSDffHandleFlags::CENTER_X_IS_SPECIAL ), true );
720 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.Second, pData->nCenterY,
721 bool( nFlags & SvxMSDffHandleFlags::CENTER_Y_IS_SPECIAL ), false );
722 rPropValues[ n ].Name = "Polar";
723 rPropValues[ n++ ].Value <<= aCenter;
724 if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
726 if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
728 css::drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum;
729 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMinimum, pData->nRangeXMin,
730 bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
731 rPropValues[ n ].Name = "RadiusRangeMinimum";
732 rPropValues[ n++ ].Value <<= aRadiusRangeMinimum;
734 if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
736 css::drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum;
737 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMaximum, pData->nRangeXMax,
738 bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
739 rPropValues[ n ].Name = "RadiusRangeMaximum";
740 rPropValues[ n++ ].Value <<= aRadiusRangeMaximum;
744 else if ( nFlags & SvxMSDffHandleFlags::RANGE )
746 if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
748 css::drawing::EnhancedCustomShapeParameter aRangeXMinimum;
749 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMinimum, pData->nRangeXMin,
750 bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
751 rPropValues[ n ].Name = "RangeXMinimum";
752 rPropValues[ n++ ].Value <<= aRangeXMinimum;
754 if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
756 css::drawing::EnhancedCustomShapeParameter aRangeXMaximum;
757 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMaximum, pData->nRangeXMax,
758 bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
759 rPropValues[ n ].Name = "RangeXMaximum";
760 rPropValues[ n++ ].Value <<= aRangeXMaximum;
762 if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
764 css::drawing::EnhancedCustomShapeParameter aRangeYMinimum;
765 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMinimum, pData->nRangeYMin,
766 bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL ), true );
767 rPropValues[ n ].Name = "RangeYMinimum";
768 rPropValues[ n++ ].Value <<= aRangeYMinimum;
770 if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
772 css::drawing::EnhancedCustomShapeParameter aRangeYMaximum;
773 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMaximum, pData->nRangeYMax,
774 bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL ), false );
775 rPropValues[ n ].Name = "RangeYMaximum";
776 rPropValues[ n++ ].Value <<= aRangeYMaximum;
781 std::unique_ptr<sdr::properties::BaseProperties> SdrObjCustomShape::CreateObjectSpecificProperties()
783 return std::make_unique<sdr::properties::CustomShapeProperties>(*this);
786 SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel)
787 : SdrTextObj(rSdrModel)
788 , fObjectRotation(0.0)
789 , mbAdjustingTextFrameWidthAndHeight(false)
790 , mpLastShadowGeometry(nullptr)
792 bClosedObj = true; // custom shapes may be filled
793 bTextFrame = true;
796 SdrObjCustomShape::~SdrObjCustomShape()
798 // delete buffered display geometry
799 InvalidateRenderGeometry();
802 void SdrObjCustomShape::MergeDefaultAttributes( const OUString* pType )
804 PropertyValue aPropVal;
805 OUString sShapeType;
806 const OUString sType( "Type" );
807 SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
808 if ( pType && !pType->isEmpty() )
810 sal_Int32 nType = pType->toInt32();
811 if ( nType )
812 sShapeType = EnhancedCustomShapeTypeNames::Get( static_cast< MSO_SPT >( nType ) );
813 else
814 sShapeType = *pType;
816 aPropVal.Name = sType;
817 aPropVal.Value <<= sShapeType;
818 aGeometryItem.SetPropertyValue( aPropVal );
820 else
822 Any *pAny = aGeometryItem.GetPropertyValueByName( sType );
823 if ( pAny )
824 *pAny >>= sShapeType;
826 MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
828 const sal_Int32* pDefData = nullptr;
829 const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
830 if ( pDefCustomShape )
831 pDefData = pDefCustomShape->pDefData;
833 css::uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > seqAdjustmentValues;
836 // AdjustmentValues
838 const OUString sAdjustmentValues( "AdjustmentValues" );
839 const Any* pAny = aGeometryItem.GetPropertyValueByName( sAdjustmentValues );
840 if ( pAny )
841 *pAny >>= seqAdjustmentValues;
842 if ( pDefCustomShape && pDefData ) // now check if we have to default some adjustment values
844 // first check if there are adjustment values are to be appended
845 sal_Int32 i, nAdjustmentValues = seqAdjustmentValues.getLength();
846 sal_Int32 nAdjustmentDefaults = *pDefData++;
847 if ( nAdjustmentDefaults > nAdjustmentValues )
849 seqAdjustmentValues.realloc( nAdjustmentDefaults );
850 for ( i = nAdjustmentValues; i < nAdjustmentDefaults; i++ )
852 seqAdjustmentValues[ i ].Value <<= pDefData[ i ];
853 seqAdjustmentValues[ i ].State = css::beans::PropertyState_DIRECT_VALUE;
856 // check if there are defaulted adjustment values that should be filled the hard coded defaults (pDefValue)
857 sal_Int32 nCount = std::min(nAdjustmentValues, nAdjustmentDefaults);
858 for ( i = 0; i < nCount; i++ )
860 if ( seqAdjustmentValues[ i ].State != css::beans::PropertyState_DIRECT_VALUE )
862 seqAdjustmentValues[ i ].Value <<= pDefData[ i ];
863 seqAdjustmentValues[ i ].State = css::beans::PropertyState_DIRECT_VALUE;
867 aPropVal.Name = sAdjustmentValues;
868 aPropVal.Value <<= seqAdjustmentValues;
869 aGeometryItem.SetPropertyValue( aPropVal );
872 // Coordsize
874 const OUString sViewBox( "ViewBox" );
875 const Any* pViewBox = aGeometryItem.GetPropertyValueByName( sViewBox );
876 css::awt::Rectangle aViewBox;
877 if ( !pViewBox || !(*pViewBox >>= aViewBox ) )
879 if ( pDefCustomShape )
881 aViewBox.X = 0;
882 aViewBox.Y = 0;
883 aViewBox.Width = pDefCustomShape->nCoordWidth;
884 aViewBox.Height= pDefCustomShape->nCoordHeight;
885 aPropVal.Name = sViewBox;
886 aPropVal.Value <<= aViewBox;
887 aGeometryItem.SetPropertyValue( aPropVal );
891 const OUString sPath( "Path" );
894 // Path/Coordinates
896 const OUString sCoordinates( "Coordinates" );
897 pAny = aGeometryItem.GetPropertyValueByName( sPath, sCoordinates );
898 if ( !pAny && pDefCustomShape && pDefCustomShape->nVertices && pDefCustomShape->pVertices )
900 css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates;
902 sal_Int32 i, nCount = pDefCustomShape->nVertices;
903 seqCoordinates.realloc( nCount );
904 for ( i = 0; i < nCount; i++ )
906 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqCoordinates[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
907 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqCoordinates[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
909 aPropVal.Name = sCoordinates;
910 aPropVal.Value <<= seqCoordinates;
911 aGeometryItem.SetPropertyValue( sPath, aPropVal );
914 // Path/GluePoints
915 const OUString sGluePoints( "GluePoints" );
916 pAny = aGeometryItem.GetPropertyValueByName( sPath, sGluePoints );
917 if ( !pAny && pDefCustomShape && pDefCustomShape->nGluePoints && pDefCustomShape->pGluePoints )
919 css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqGluePoints;
920 sal_Int32 i, nCount = pDefCustomShape->nGluePoints;
921 seqGluePoints.realloc( nCount );
922 for ( i = 0; i < nCount; i++ )
924 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqGluePoints[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
925 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqGluePoints[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
927 aPropVal.Name = sGluePoints;
928 aPropVal.Value <<= seqGluePoints;
929 aGeometryItem.SetPropertyValue( sPath, aPropVal );
932 // Path/Segments
933 const OUString sSegments( "Segments" );
934 pAny = aGeometryItem.GetPropertyValueByName( sPath, sSegments );
935 if ( !pAny && pDefCustomShape && pDefCustomShape->nElements && pDefCustomShape->pElements )
937 css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > seqSegments;
939 sal_Int32 i, nCount = pDefCustomShape->nElements;
940 seqSegments.realloc( nCount );
941 for ( i = 0; i < nCount; i++ )
943 EnhancedCustomShapeSegment& rSegInfo = seqSegments[ i ];
944 sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
945 lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
947 aPropVal.Name = sSegments;
948 aPropVal.Value <<= seqSegments;
949 aGeometryItem.SetPropertyValue( sPath, aPropVal );
952 // Path/StretchX
953 const OUString sStretchX( "StretchX" );
954 pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchX );
955 if ( !pAny && pDefCustomShape )
957 sal_Int32 nXRef = pDefCustomShape->nXRef;
958 if ( nXRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
960 aPropVal.Name = sStretchX;
961 aPropVal.Value <<= nXRef;
962 aGeometryItem.SetPropertyValue( sPath, aPropVal );
966 // Path/StretchY
967 const OUString sStretchY( "StretchY" );
968 pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchY );
969 if ( !pAny && pDefCustomShape )
971 sal_Int32 nYRef = pDefCustomShape->nYRef;
972 if ( nYRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
974 aPropVal.Name = sStretchY;
975 aPropVal.Value <<= nYRef;
976 aGeometryItem.SetPropertyValue( sPath, aPropVal );
980 // Path/TextFrames
981 const OUString sTextFrames( "TextFrames" );
982 pAny = aGeometryItem.GetPropertyValueByName( sPath, sTextFrames );
983 if ( !pAny && pDefCustomShape && pDefCustomShape->nTextRect && pDefCustomShape->pTextRect )
985 css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > seqTextFrames;
987 sal_Int32 i, nCount = pDefCustomShape->nTextRect;
988 seqTextFrames.realloc( nCount );
989 const SvxMSDffTextRectangles* pRectangles = pDefCustomShape->pTextRect;
990 for ( i = 0; i < nCount; i++, pRectangles++ )
992 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqTextFrames[ i ].TopLeft.First, pRectangles->nPairA.nValA );
993 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqTextFrames[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
994 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqTextFrames[ i ].BottomRight.First, pRectangles->nPairB.nValA );
995 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqTextFrames[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
997 aPropVal.Name = sTextFrames;
998 aPropVal.Value <<= seqTextFrames;
999 aGeometryItem.SetPropertyValue( sPath, aPropVal );
1002 // Equations
1003 const OUString sEquations( "Equations" );
1004 pAny = aGeometryItem.GetPropertyValueByName( sEquations );
1005 if ( !pAny && pDefCustomShape && pDefCustomShape->nCalculation && pDefCustomShape->pCalculation )
1007 css::uno::Sequence< OUString > seqEquations;
1009 sal_Int32 i, nCount = pDefCustomShape->nCalculation;
1010 seqEquations.realloc( nCount );
1011 const SvxMSDffCalculationData* pData = pDefCustomShape->pCalculation;
1012 for ( i = 0; i < nCount; i++, pData++ )
1013 seqEquations[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
1014 aPropVal.Name = sEquations;
1015 aPropVal.Value <<= seqEquations;
1016 aGeometryItem.SetPropertyValue( aPropVal );
1019 // Handles
1020 const OUString sHandles( "Handles" );
1021 pAny = aGeometryItem.GetPropertyValueByName( sHandles );
1022 if ( !pAny && pDefCustomShape && pDefCustomShape->nHandles && pDefCustomShape->pHandles )
1024 css::uno::Sequence< css::beans::PropertyValues > seqHandles;
1026 sal_Int32 i, nCount = pDefCustomShape->nHandles;
1027 const SvxMSDffHandle* pData = pDefCustomShape->pHandles;
1028 seqHandles.realloc( nCount );
1029 for ( i = 0; i < nCount; i++, pData++ )
1031 sal_Int32 nPropertiesNeeded;
1032 css::beans::PropertyValues& rPropValues = seqHandles[ i ];
1033 nPropertiesNeeded = GetNumberOfProperties( pData );
1034 rPropValues.realloc( nPropertiesNeeded );
1035 lcl_ShapePropertiesFromDFF( pData, rPropValues );
1037 aPropVal.Name = sHandles;
1038 aPropVal.Value <<= seqHandles;
1039 aGeometryItem.SetPropertyValue( aPropVal );
1041 else if (pAny && sShapeType.startsWith("ooxml-") && sShapeType != "ooxml-non-primitive")
1043 // ODF is not able to store the ooxml way of connecting handle to an adjustment
1044 // value by name, e.g. attribute RefX="adj". So the information is lost, when exporting
1045 // a pptx to odp, for example. This part reconstructs this information for the
1046 // ooxml preset shapes from their definition.
1047 css::uno::Sequence<css::beans::PropertyValues> seqHandles;
1048 *pAny >>= seqHandles;
1049 bool bChanged(false);
1050 for (sal_Int32 i = 0; i < seqHandles.getLength(); i++)
1052 comphelper::SequenceAsHashMap aHandleProps(seqHandles[i]);
1053 OUString sFirstRefType;
1054 sal_Int32 nFirstAdjRef;
1055 OUString sSecondRefType;
1056 sal_Int32 nSecondAdjRef;
1057 PresetOOXHandleAdj::GetOOXHandleAdjRelation(sShapeType, i, sFirstRefType, nFirstAdjRef,
1058 sSecondRefType, nSecondAdjRef);
1059 if (sFirstRefType != "na" && 0 <= nFirstAdjRef
1060 && nFirstAdjRef < seqAdjustmentValues.getLength())
1062 bChanged |= aHandleProps.createItemIfMissing(sFirstRefType, nFirstAdjRef);
1064 if (sSecondRefType != "na" && 0 <= nSecondAdjRef
1065 && nSecondAdjRef < seqAdjustmentValues.getLength())
1067 bChanged |= aHandleProps.createItemIfMissing(sSecondRefType, nSecondAdjRef);
1069 aHandleProps >> seqHandles[i];
1071 if (bChanged)
1073 aPropVal.Name = sHandles;
1074 aPropVal.Value <<= seqHandles;
1075 aGeometryItem.SetPropertyValue(aPropVal);
1079 SetMergedItem( aGeometryItem );
1082 bool SdrObjCustomShape::IsDefaultGeometry( const DefaultType eDefaultType ) const
1084 bool bIsDefaultGeometry = false;
1086 OUString sShapeType;
1087 const SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
1089 const Any *pAny = aGeometryItem.GetPropertyValueByName( "Type" );
1090 if ( pAny )
1091 *pAny >>= sShapeType;
1093 MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
1095 const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
1096 const OUString sPath( "Path" );
1097 switch( eDefaultType )
1099 case DefaultType::Viewbox :
1101 const Any* pViewBox = const_cast<SdrCustomShapeGeometryItem&>(aGeometryItem).GetPropertyValueByName( "ViewBox" );
1102 css::awt::Rectangle aViewBox;
1103 if (pViewBox && (*pViewBox >>= aViewBox) && pDefCustomShape)
1105 if ( ( aViewBox.Width == pDefCustomShape->nCoordWidth )
1106 && ( aViewBox.Height == pDefCustomShape->nCoordHeight ) )
1107 bIsDefaultGeometry = true;
1110 break;
1112 case DefaultType::Path :
1114 pAny = const_cast<SdrCustomShapeGeometryItem&>(aGeometryItem).GetPropertyValueByName( sPath, "Coordinates" );
1115 if ( pAny && pDefCustomShape && pDefCustomShape->nVertices && pDefCustomShape->pVertices )
1117 css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates1, seqCoordinates2;
1118 if ( *pAny >>= seqCoordinates1 )
1120 sal_Int32 i, nCount = pDefCustomShape->nVertices;
1121 seqCoordinates2.realloc( nCount );
1122 for ( i = 0; i < nCount; i++ )
1124 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqCoordinates2[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
1125 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqCoordinates2[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
1127 if ( seqCoordinates1 == seqCoordinates2 )
1128 bIsDefaultGeometry = true;
1131 else if ( pDefCustomShape && ( ( pDefCustomShape->nVertices == 0 ) || ( pDefCustomShape->pVertices == nullptr ) ) )
1132 bIsDefaultGeometry = true;
1134 break;
1136 case DefaultType::Gluepoints :
1138 pAny = const_cast<SdrCustomShapeGeometryItem&>(aGeometryItem).GetPropertyValueByName( sPath, "GluePoints" );
1139 if ( pAny && pDefCustomShape && pDefCustomShape->nGluePoints && pDefCustomShape->pGluePoints )
1141 css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqGluePoints1, seqGluePoints2;
1142 if ( *pAny >>= seqGluePoints1 )
1144 sal_Int32 i, nCount = pDefCustomShape->nGluePoints;
1145 seqGluePoints2.realloc( nCount );
1146 for ( i = 0; i < nCount; i++ )
1148 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqGluePoints2[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
1149 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqGluePoints2[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
1151 if ( seqGluePoints1 == seqGluePoints2 )
1152 bIsDefaultGeometry = true;
1155 else if ( pDefCustomShape && ( pDefCustomShape->nGluePoints == 0 ) )
1156 bIsDefaultGeometry = true;
1158 break;
1160 case DefaultType::Segments :
1162 // Path/Segments
1163 pAny = const_cast<SdrCustomShapeGeometryItem&>(aGeometryItem).GetPropertyValueByName( sPath, "Segments" );
1164 if ( pAny )
1166 css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > seqSegments1, seqSegments2;
1167 if ( *pAny >>= seqSegments1 )
1169 if ( pDefCustomShape && pDefCustomShape->nElements && pDefCustomShape->pElements )
1171 sal_Int32 i, nCount = pDefCustomShape->nElements;
1172 if ( nCount )
1174 seqSegments2.realloc( nCount );
1175 for ( i = 0; i < nCount; i++ )
1177 EnhancedCustomShapeSegment& rSegInfo = seqSegments2[ i ];
1178 sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
1179 lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
1181 if ( seqSegments1 == seqSegments2 )
1182 bIsDefaultGeometry = true;
1185 else
1187 // check if it's the default segment description ( M L Z N )
1188 if ( seqSegments1.getLength() == 4 )
1190 if ( ( seqSegments1[ 0 ].Command == EnhancedCustomShapeSegmentCommand::MOVETO )
1191 && ( seqSegments1[ 1 ].Command == EnhancedCustomShapeSegmentCommand::LINETO )
1192 && ( seqSegments1[ 2 ].Command == EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
1193 && ( seqSegments1[ 3 ].Command == EnhancedCustomShapeSegmentCommand::ENDSUBPATH ) )
1194 bIsDefaultGeometry = true;
1199 else if ( pDefCustomShape && ( ( pDefCustomShape->nElements == 0 ) || ( pDefCustomShape->pElements == nullptr ) ) )
1200 bIsDefaultGeometry = true;
1202 break;
1204 case DefaultType::StretchX :
1206 pAny = const_cast<SdrCustomShapeGeometryItem&>(aGeometryItem).GetPropertyValueByName( sPath, "StretchX" );
1207 if ( pAny && pDefCustomShape )
1209 sal_Int32 nStretchX = 0;
1210 if ( *pAny >>= nStretchX )
1212 if ( pDefCustomShape->nXRef == nStretchX )
1213 bIsDefaultGeometry = true;
1216 else if ( pDefCustomShape && ( pDefCustomShape->nXRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
1217 bIsDefaultGeometry = true;
1219 break;
1221 case DefaultType::StretchY :
1223 pAny = const_cast<SdrCustomShapeGeometryItem&>(aGeometryItem).GetPropertyValueByName( sPath, "StretchY" );
1224 if ( pAny && pDefCustomShape )
1226 sal_Int32 nStretchY = 0;
1227 if ( *pAny >>= nStretchY )
1229 if ( pDefCustomShape->nYRef == nStretchY )
1230 bIsDefaultGeometry = true;
1233 else if ( pDefCustomShape && ( pDefCustomShape->nYRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
1234 bIsDefaultGeometry = true;
1236 break;
1238 case DefaultType::Equations :
1240 pAny = const_cast<SdrCustomShapeGeometryItem&>(aGeometryItem).GetPropertyValueByName( "Equations" );
1241 if ( pAny && pDefCustomShape && pDefCustomShape->nCalculation && pDefCustomShape->pCalculation )
1243 css::uno::Sequence< OUString > seqEquations1, seqEquations2;
1244 if ( *pAny >>= seqEquations1 )
1246 sal_Int32 i, nCount = pDefCustomShape->nCalculation;
1247 seqEquations2.realloc( nCount );
1249 const SvxMSDffCalculationData* pData = pDefCustomShape->pCalculation;
1250 for ( i = 0; i < nCount; i++, pData++ )
1251 seqEquations2[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
1253 if ( seqEquations1 == seqEquations2 )
1254 bIsDefaultGeometry = true;
1257 else if ( pDefCustomShape && ( ( pDefCustomShape->nCalculation == 0 ) || ( pDefCustomShape->pCalculation == nullptr ) ) )
1258 bIsDefaultGeometry = true;
1260 break;
1262 case DefaultType::TextFrames :
1264 pAny = const_cast<SdrCustomShapeGeometryItem&>(aGeometryItem).GetPropertyValueByName( sPath, "TextFrames" );
1265 if ( pAny && pDefCustomShape && pDefCustomShape->nTextRect && pDefCustomShape->pTextRect )
1267 css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > seqTextFrames1, seqTextFrames2;
1268 if ( *pAny >>= seqTextFrames1 )
1270 sal_Int32 i, nCount = pDefCustomShape->nTextRect;
1271 seqTextFrames2.realloc( nCount );
1272 const SvxMSDffTextRectangles* pRectangles = pDefCustomShape->pTextRect;
1273 for ( i = 0; i < nCount; i++, pRectangles++ )
1275 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqTextFrames2[ i ].TopLeft.First, pRectangles->nPairA.nValA );
1276 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqTextFrames2[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
1277 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqTextFrames2[ i ].BottomRight.First, pRectangles->nPairB.nValA );
1278 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqTextFrames2[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
1280 if ( seqTextFrames1 == seqTextFrames2 )
1281 bIsDefaultGeometry = true;
1284 else if ( pDefCustomShape && ( ( pDefCustomShape->nTextRect == 0 ) || ( pDefCustomShape->pTextRect == nullptr ) ) )
1285 bIsDefaultGeometry = true;
1287 break;
1289 return bIsDefaultGeometry;
1292 void SdrObjCustomShape::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
1294 rInfo.bResizeFreeAllowed=fObjectRotation == 0.0;
1295 rInfo.bResizePropAllowed=true;
1296 rInfo.bRotateFreeAllowed=true;
1297 rInfo.bRotate90Allowed =true;
1298 rInfo.bMirrorFreeAllowed=true;
1299 rInfo.bMirror45Allowed =true;
1300 rInfo.bMirror90Allowed =true;
1301 rInfo.bTransparenceAllowed = false;
1302 rInfo.bShearAllowed =true;
1303 rInfo.bEdgeRadiusAllowed=false;
1304 rInfo.bNoContortion =true;
1306 // #i37011#
1307 if ( !mXRenderedCustomShape.is() )
1308 return;
1310 const SdrObject* pRenderedCustomShape = GetSdrObjectFromXShape( mXRenderedCustomShape );
1311 if ( !pRenderedCustomShape )
1312 return;
1314 // #i37262#
1315 // Iterate self over the contained objects, since there are combinations of
1316 // polygon and curve objects. In that case, aInfo.bCanConvToPath and
1317 // aInfo.bCanConvToPoly would be false. What is needed here is an or, not an and.
1318 SdrObjListIter aIterator(*pRenderedCustomShape);
1319 while(aIterator.IsMore())
1321 SdrObject* pCandidate = aIterator.Next();
1322 SdrObjTransformInfoRec aInfo;
1323 pCandidate->TakeObjInfo(aInfo);
1325 // set path and poly conversion if one is possible since
1326 // this object will first be broken
1327 const bool bCanConvToPathOrPoly(aInfo.bCanConvToPath || aInfo.bCanConvToPoly);
1328 if(rInfo.bCanConvToPath != bCanConvToPathOrPoly)
1330 rInfo.bCanConvToPath = bCanConvToPathOrPoly;
1333 if(rInfo.bCanConvToPoly != bCanConvToPathOrPoly)
1335 rInfo.bCanConvToPoly = bCanConvToPathOrPoly;
1338 if(rInfo.bCanConvToContour != aInfo.bCanConvToContour)
1340 rInfo.bCanConvToContour = aInfo.bCanConvToContour;
1343 if(rInfo.bShearAllowed != aInfo.bShearAllowed)
1345 rInfo.bShearAllowed = aInfo.bShearAllowed;
1350 SdrObjKind SdrObjCustomShape::GetObjIdentifier() const
1352 return OBJ_CUSTOMSHAPE;
1355 // #115391# This implementation is based on the TextFrame size of the CustomShape and the
1356 // state of the ResizeShapeToFitText flag to correctly set TextMinFrameWidth/Height
1357 void SdrObjCustomShape::AdaptTextMinSize()
1359 if (getSdrModelFromSdrObject().IsCreatingDataObj() || getSdrModelFromSdrObject().IsPasteResize())
1360 return;
1362 const bool bResizeShapeToFitText(GetObjectItem(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue());
1363 SfxItemSet aSet(
1364 *GetObjectItemSet().GetPool(),
1365 svl::Items<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
1366 SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH>{}); // contains SDRATTR_TEXT_MAXFRAMEWIDTH
1367 bool bChanged(false);
1369 if(bResizeShapeToFitText)
1371 // always reset MinWidthHeight to zero to only rely on text size and frame size
1372 // to allow resizing being completely dependent on text size only
1373 aSet.Put(makeSdrTextMinFrameWidthItem(0));
1374 aSet.Put(makeSdrTextMinFrameHeightItem(0));
1375 bChanged = true;
1377 else
1379 // recreate from CustomShape-specific TextBounds
1380 tools::Rectangle aTextBound(maRect);
1382 if(GetTextBounds(aTextBound))
1384 const tools::Long nHDist(GetTextLeftDistance() + GetTextRightDistance());
1385 const tools::Long nVDist(GetTextUpperDistance() + GetTextLowerDistance());
1386 const tools::Long nTWdt(std::max(tools::Long(0), static_cast<tools::Long>(aTextBound.GetWidth() - 1 - nHDist)));
1387 const tools::Long nTHgt(std::max(tools::Long(0), static_cast<tools::Long>(aTextBound.GetHeight() - 1 - nVDist)));
1389 aSet.Put(makeSdrTextMinFrameWidthItem(nTWdt));
1390 aSet.Put(makeSdrTextMinFrameHeightItem(nTHgt));
1391 bChanged = true;
1395 if(bChanged)
1396 SetObjectItemSet(aSet);
1399 void SdrObjCustomShape::NbcSetSnapRect( const tools::Rectangle& rRect )
1401 maRect = rRect;
1402 ImpJustifyRect(maRect);
1403 InvalidateRenderGeometry();
1405 AdaptTextMinSize();
1407 ImpCheckShear();
1408 SetRectsDirty();
1409 SetChanged();
1412 void SdrObjCustomShape::SetSnapRect( const tools::Rectangle& rRect )
1414 tools::Rectangle aBoundRect0;
1415 if ( pUserCall )
1416 aBoundRect0 = GetLastBoundRect();
1417 NbcSetSnapRect( rRect );
1418 BroadcastObjectChange();
1419 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
1422 void SdrObjCustomShape::NbcSetLogicRect( const tools::Rectangle& rRect )
1424 maRect = rRect;
1425 ImpJustifyRect(maRect);
1426 InvalidateRenderGeometry();
1428 AdaptTextMinSize();
1430 SetRectsDirty();
1431 SetChanged();
1434 void SdrObjCustomShape::SetLogicRect( const tools::Rectangle& rRect )
1436 tools::Rectangle aBoundRect0;
1437 if ( pUserCall )
1438 aBoundRect0 = GetLastBoundRect();
1439 NbcSetLogicRect(rRect);
1440 BroadcastObjectChange();
1441 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
1444 void SdrObjCustomShape::Move( const Size& rSiz )
1446 if ( rSiz.Width() || rSiz.Height() )
1448 tools::Rectangle aBoundRect0;
1449 if ( pUserCall )
1450 aBoundRect0 = GetLastBoundRect();
1451 NbcMove(rSiz);
1452 SetChanged();
1453 BroadcastObjectChange();
1454 SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
1457 void SdrObjCustomShape::NbcMove( const Size& rSiz )
1459 SdrTextObj::NbcMove( rSiz );
1460 if ( mXRenderedCustomShape.is() )
1462 SdrObject* pRenderedCustomShape = GetSdrObjectFromXShape( mXRenderedCustomShape );
1463 if ( pRenderedCustomShape )
1465 // #i97149# the visualisation shape needs to be informed
1466 // about change, too
1467 pRenderedCustomShape->ActionChanged();
1468 pRenderedCustomShape->NbcMove( rSiz );
1472 // #i37011# adapt geometry shadow
1473 if(mpLastShadowGeometry)
1475 mpLastShadowGeometry->NbcMove( rSiz );
1479 void SdrObjCustomShape::NbcResize( const Point& rRef, const Fraction& rxFact, const Fraction& ryFact )
1481 // taking care of handles that should not been changed
1482 tools::Rectangle aOld( maRect );
1483 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
1485 SdrTextObj::NbcResize( rRef, rxFact, ryFact );
1487 if ( ( rxFact.GetNumerator() != rxFact.GetDenominator() )
1488 || ( ryFact.GetNumerator()!= ryFact.GetDenominator() ) )
1490 if ( ( ( rxFact.GetNumerator() < 0 ) && ( rxFact.GetDenominator() > 0 ) ) ||
1491 ( ( rxFact.GetNumerator() > 0 ) && ( rxFact.GetDenominator() < 0 ) ) )
1493 SetMirroredX( !IsMirroredX() );
1495 if ( ( ( ryFact.GetNumerator() < 0 ) && ( ryFact.GetDenominator() > 0 ) ) ||
1496 ( ( ryFact.GetNumerator() > 0 ) && ( ryFact.GetDenominator() < 0 ) ) )
1498 SetMirroredY( !IsMirroredY() );
1502 for (const auto& rInteraction : aInteractionHandles)
1506 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
1507 rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
1508 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X )
1510 sal_Int32 nX = ( rInteraction.aPosition.X - aOld.Left() ) + maRect.Left();
1511 rInteraction.xInteraction->setControllerPosition( css::awt::Point( nX, rInteraction.xInteraction->getPosition().Y ) );
1513 else if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
1515 sal_Int32 nX = maRect.Right() - (aOld.Right() - rInteraction.aPosition.X);
1516 rInteraction.xInteraction->setControllerPosition( css::awt::Point( nX, rInteraction.xInteraction->getPosition().Y ) );
1518 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
1520 sal_Int32 nY = ( rInteraction.aPosition.Y - aOld.Top() ) + maRect.Top();
1521 rInteraction.xInteraction->setControllerPosition( css::awt::Point( rInteraction.xInteraction->getPosition().X, nY ) );
1524 catch ( const uno::RuntimeException& )
1529 // updating fObjectRotation
1530 tools::Long nTextObjRotation = aGeo.nRotationAngle;
1531 double fAngle = nTextObjRotation;
1532 fAngle /= 100.0;
1533 if (IsMirroredX())
1535 if (IsMirroredY())
1536 fObjectRotation = fAngle - 180.0;
1537 else
1538 fObjectRotation = -fAngle;
1540 else
1542 if (IsMirroredY())
1543 fObjectRotation = 180.0 - fAngle;
1544 else
1545 fObjectRotation = fAngle;
1547 while (fObjectRotation < 0)
1548 fObjectRotation += 360.0;
1549 while (fObjectRotation >= 360.0)
1550 fObjectRotation -= 360.0;
1552 InvalidateRenderGeometry();
1555 void SdrObjCustomShape::NbcRotate( const Point& rRef, tools::Long nAngle, double sn, double cs )
1557 bool bMirroredX = IsMirroredX();
1558 bool bMirroredY = IsMirroredY();
1560 fObjectRotation = fmod( fObjectRotation, 360.0 );
1561 if ( fObjectRotation < 0 )
1562 fObjectRotation = 360 + fObjectRotation;
1564 // the rotation angle for ashapes is stored in fObjectRotation, this rotation
1565 // has to be applied to the text object (which is internally using aGeo.nAngle).
1566 SdrTextObj::NbcRotate( maRect.TopLeft(), -aGeo.nRotationAngle, // retrieving the unrotated text object
1567 sin( (-aGeo.nRotationAngle) * F_PI18000 ),
1568 cos( (-aGeo.nRotationAngle) * F_PI18000 ) );
1569 aGeo.nRotationAngle = 0; // resetting aGeo data
1570 aGeo.RecalcSinCos();
1572 tools::Long nW = static_cast<tools::Long>( fObjectRotation * 100 ); // applying our object rotation
1573 if ( bMirroredX )
1574 nW = 36000 - nW;
1575 if ( bMirroredY )
1576 nW = 18000 - nW;
1577 nW = nW % 36000;
1578 if ( nW < 0 )
1579 nW = 36000 + nW;
1580 SdrTextObj::NbcRotate( maRect.TopLeft(), nW, // applying text rotation
1581 sin( nW * F_PI18000 ),
1582 cos( nW * F_PI18000 ) );
1584 int nSwap = 0;
1585 if ( bMirroredX )
1586 nSwap ^= 1;
1587 if ( bMirroredY )
1588 nSwap ^= 1;
1590 double fAngle = nAngle; // updating to our new object rotation
1591 fAngle /= 100.0;
1592 fObjectRotation = fmod( nSwap ? fObjectRotation - fAngle : fObjectRotation + fAngle, 360.0 );
1593 if ( fObjectRotation < 0 )
1594 fObjectRotation = 360 + fObjectRotation;
1596 SdrTextObj::NbcRotate( rRef, nAngle, sn, cs ); // applying text rotation
1597 InvalidateRenderGeometry();
1600 void SdrObjCustomShape::NbcMirror( const Point& rRef1, const Point& rRef2 )
1602 // TTTT: Fix for old mirroring, can be removed again in aw080
1603 // storing horizontal and vertical flipping without modifying the rotate angle
1604 // decompose other flipping to rotation and MirrorX.
1605 tools::Long ndx = rRef2.X()-rRef1.X();
1606 tools::Long ndy = rRef2.Y()-rRef1.Y();
1608 if(!ndx) // MirroredX
1610 SetMirroredX(!IsMirroredX());
1611 SdrTextObj::NbcMirror( rRef1, rRef2 );
1613 else
1615 if(!ndy) // MirroredY
1617 SetMirroredY(!IsMirroredY());
1618 SdrTextObj::NbcMirror( rRef1, rRef2 );
1620 else // neither horizontal nor vertical
1622 SetMirroredX(!IsMirroredX());
1624 // call parent
1625 SdrTextObj::NbcMirror( rRef1, rRef2 );
1627 // update fObjectRotation
1628 tools::Long nTextObjRotation = aGeo.nRotationAngle;
1629 double fAngle = nTextObjRotation;
1631 fAngle /= 100.0;
1633 bool bSingleFlip = (IsMirroredX()!= IsMirroredY());
1635 fObjectRotation = fmod( bSingleFlip ? -fAngle : fAngle, 360.0 );
1637 if ( fObjectRotation < 0 )
1639 fObjectRotation = 360.0 + fObjectRotation;
1644 InvalidateRenderGeometry();
1647 void SdrObjCustomShape::Shear( const Point& rRef, tools::Long nAngle, double tn, bool bVShear )
1649 SdrTextObj::Shear( rRef, nAngle, tn, bVShear );
1650 InvalidateRenderGeometry();
1652 void SdrObjCustomShape::NbcShear( const Point& rRef, tools::Long nAngle, double tn, bool bVShear )
1654 // TTTT: Fix for old mirroring, can be removed again in aw080
1655 SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
1657 // updating fObjectRotation
1658 tools::Long nTextObjRotation = aGeo.nRotationAngle;
1659 double fAngle = nTextObjRotation;
1660 fAngle /= 100.0;
1661 if (IsMirroredX())
1663 if (IsMirroredY())
1664 fObjectRotation = fAngle - 180.0;
1665 else
1666 fObjectRotation = -fAngle;
1668 else
1670 if (IsMirroredY())
1671 fObjectRotation = 180.0 - fAngle;
1672 else
1673 fObjectRotation = fAngle;
1675 while (fObjectRotation < 0)
1676 fObjectRotation += 360.0;
1677 while (fObjectRotation >= 360.0)
1678 fObjectRotation -= 360.0;
1680 InvalidateRenderGeometry();
1683 SdrGluePoint SdrObjCustomShape::GetVertexGluePoint(sal_uInt16 nPosNum) const
1685 sal_Int32 nWdt = ImpGetLineWdt(); // #i25616#
1687 // #i25616#
1688 if(!LineIsOutsideGeometry())
1690 nWdt++;
1691 nWdt /= 2;
1694 Point aPt;
1695 switch (nPosNum) {
1696 case 0: aPt=maRect.TopCenter(); aPt.AdjustY( -nWdt ); break;
1697 case 1: aPt=maRect.RightCenter(); aPt.AdjustX(nWdt ); break;
1698 case 2: aPt=maRect.BottomCenter(); aPt.AdjustY(nWdt ); break;
1699 case 3: aPt=maRect.LeftCenter(); aPt.AdjustX( -nWdt ); break;
1701 if (aGeo.nShearAngle!=0) ShearPoint(aPt,maRect.TopLeft(),aGeo.nTan);
1702 if (aGeo.nRotationAngle!=0) RotatePoint(aPt,maRect.TopLeft(),aGeo.nSin,aGeo.nCos);
1703 aPt-=GetSnapRect().Center();
1704 SdrGluePoint aGP(aPt);
1705 aGP.SetPercent(false);
1706 return aGP;
1710 // #i38892#
1711 void SdrObjCustomShape::ImpCheckCustomGluePointsAreAdded()
1713 const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
1715 if(!pSdrObject)
1716 return;
1718 const SdrGluePointList* pSource = pSdrObject->GetGluePointList();
1720 if(!(pSource && pSource->GetCount()))
1721 return;
1723 if(!SdrTextObj::GetGluePointList())
1725 SdrTextObj::ForceGluePointList();
1728 const SdrGluePointList* pList = SdrTextObj::GetGluePointList();
1730 if(!pList)
1731 return;
1733 SdrGluePointList aNewList;
1734 sal_uInt16 a;
1736 for(a = 0; a < pSource->GetCount(); a++)
1738 SdrGluePoint aCopy((*pSource)[a]);
1739 aCopy.SetUserDefined(false);
1740 aNewList.Insert(aCopy);
1743 bool bMirroredX = IsMirroredX();
1744 bool bMirroredY = IsMirroredY();
1746 tools::Long nShearAngle = aGeo.nShearAngle;
1747 double fTan = aGeo.nTan;
1749 if ( aGeo.nRotationAngle || nShearAngle || bMirroredX || bMirroredY )
1751 tools::Polygon aPoly( maRect );
1752 if( nShearAngle )
1754 sal_uInt16 nPointCount=aPoly.GetSize();
1755 for (sal_uInt16 i=0; i<nPointCount; i++)
1756 ShearPoint(aPoly[i],maRect.Center(), fTan );
1758 if ( aGeo.nRotationAngle )
1759 aPoly.Rotate( maRect.Center(), Degree10(aGeo.nRotationAngle / 10) );
1761 tools::Rectangle aBoundRect( aPoly.GetBoundRect() );
1762 sal_Int32 nXDiff = aBoundRect.Left() - maRect.Left();
1763 sal_Int32 nYDiff = aBoundRect.Top() - maRect.Top();
1765 if (nShearAngle && bMirroredX != bMirroredY)
1767 nShearAngle = -nShearAngle;
1768 fTan = -fTan;
1771 Point aRef( maRect.GetWidth() / 2, maRect.GetHeight() / 2 );
1772 for ( a = 0; a < aNewList.GetCount(); a++ )
1774 SdrGluePoint& rPoint = aNewList[ a ];
1775 Point aGlue( rPoint.GetPos() );
1776 if ( nShearAngle )
1777 ShearPoint( aGlue, aRef, fTan );
1779 RotatePoint(aGlue, aRef, sin(basegfx::deg2rad(fObjectRotation)),
1780 cos(basegfx::deg2rad(fObjectRotation)));
1781 if ( bMirroredX )
1782 aGlue.setX( maRect.GetWidth() - aGlue.X() );
1783 if ( bMirroredY )
1784 aGlue.setY( maRect.GetHeight() - aGlue.Y() );
1785 aGlue.AdjustX( -nXDiff );
1786 aGlue.AdjustY( -nYDiff );
1787 rPoint.SetPos( aGlue );
1791 for(a = 0; a < pList->GetCount(); a++)
1793 const SdrGluePoint& rCandidate = (*pList)[a];
1795 if(rCandidate.IsUserDefined())
1797 aNewList.Insert(rCandidate);
1801 // copy new list to local. This is NOT very convenient behavior, the local
1802 // GluePointList should not be set, but we delivered by using GetGluePointList(),
1803 // maybe on demand. Since the local object is changed here, this is assumed to
1804 // be a result of GetGluePointList and thus the list is copied
1805 if(pPlusData)
1807 pPlusData->SetGluePoints(aNewList);
1811 // #i38892#
1812 const SdrGluePointList* SdrObjCustomShape::GetGluePointList() const
1814 const_cast<SdrObjCustomShape*>(this)->ImpCheckCustomGluePointsAreAdded();
1815 return SdrTextObj::GetGluePointList();
1818 // #i38892#
1819 SdrGluePointList* SdrObjCustomShape::ForceGluePointList()
1821 if(SdrTextObj::ForceGluePointList())
1823 ImpCheckCustomGluePointsAreAdded();
1824 return SdrTextObj::ForceGluePointList();
1826 else
1828 return nullptr;
1833 sal_uInt32 SdrObjCustomShape::GetHdlCount() const
1835 const sal_uInt32 nBasicHdlCount(SdrTextObj::GetHdlCount());
1836 return ( GetInteractionHandles().size() + nBasicHdlCount );
1839 void SdrObjCustomShape::AddToHdlList(SdrHdlList& rHdlList) const
1841 SdrTextObj::AddToHdlList(rHdlList);
1843 int nCustomShapeHdlNum = 0;
1844 for (SdrCustomShapeInteraction const & rInteraction : GetInteractionHandles())
1846 if ( rInteraction.xInteraction.is() )
1850 css::awt::Point aPosition( rInteraction.xInteraction->getPosition() );
1851 std::unique_ptr<SdrHdl> pH(new SdrHdl( Point( aPosition.X, aPosition.Y ), SdrHdlKind::CustomShape1 ));
1852 pH->SetPointNum( nCustomShapeHdlNum );
1853 pH->SetObj( const_cast<SdrObjCustomShape*>(this) );
1854 rHdlList.AddHdl(std::move(pH));
1856 catch ( const uno::RuntimeException& )
1860 ++nCustomShapeHdlNum;
1864 bool SdrObjCustomShape::hasSpecialDrag() const
1866 return true;
1869 bool SdrObjCustomShape::beginSpecialDrag(SdrDragStat& rDrag) const
1871 const SdrHdl* pHdl = rDrag.GetHdl();
1873 if(pHdl && SdrHdlKind::CustomShape1 == pHdl->GetKind())
1875 rDrag.SetEndDragChangesAttributes(true);
1876 rDrag.SetNoSnap();
1878 else
1880 const SdrHdl* pHdl2 = rDrag.GetHdl();
1881 const SdrHdlKind eHdl((pHdl2 == nullptr) ? SdrHdlKind::Move : pHdl2->GetKind());
1883 switch( eHdl )
1885 case SdrHdlKind::UpperLeft :
1886 case SdrHdlKind::Upper :
1887 case SdrHdlKind::UpperRight :
1888 case SdrHdlKind::Left :
1889 case SdrHdlKind::Right :
1890 case SdrHdlKind::LowerLeft :
1891 case SdrHdlKind::Lower :
1892 case SdrHdlKind::LowerRight :
1893 case SdrHdlKind::Move :
1895 break;
1897 default:
1899 return false;
1904 return true;
1907 void SdrObjCustomShape::DragResizeCustomShape( const tools::Rectangle& rNewRect )
1909 tools::Rectangle aOld( maRect );
1910 bool bOldMirroredX( IsMirroredX() );
1911 bool bOldMirroredY( IsMirroredY() );
1913 tools::Rectangle aNewRect( rNewRect );
1914 aNewRect.Justify();
1916 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
1918 GeoStat aGeoStat( GetGeoStat() );
1919 if ( aNewRect.TopLeft()!= maRect.TopLeft() &&
1920 ( aGeo.nRotationAngle || aGeo.nShearAngle ) )
1922 Point aNewPos( aNewRect.TopLeft() );
1923 if ( aGeo.nShearAngle ) ShearPoint( aNewPos, aOld.TopLeft(), aGeoStat.nTan );
1924 if ( aGeo.nRotationAngle ) RotatePoint(aNewPos, aOld.TopLeft(), aGeoStat.nSin, aGeoStat.nCos );
1925 aNewRect.SetPos( aNewPos );
1927 if ( aNewRect == maRect )
1928 return;
1930 SetLogicRect( aNewRect );
1931 InvalidateRenderGeometry();
1933 if ( rNewRect.Left() > rNewRect.Right() )
1935 Point aTop( ( GetSnapRect().Left() + GetSnapRect().Right() ) >> 1, GetSnapRect().Top() );
1936 Point aBottom( aTop.X(), aTop.Y() + 1000 );
1937 NbcMirror( aTop, aBottom );
1939 if ( rNewRect.Top() > rNewRect.Bottom() )
1941 Point aLeft( GetSnapRect().Left(), ( GetSnapRect().Top() + GetSnapRect().Bottom() ) >> 1 );
1942 Point aRight( aLeft.X() + 1000, aLeft.Y() );
1943 NbcMirror( aLeft, aRight );
1946 for (const auto& rInteraction : aInteractionHandles)
1950 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
1951 rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
1952 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X ||
1953 rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
1955 if (rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX)
1956 bOldMirroredX = !bOldMirroredX;
1958 sal_Int32 nX;
1959 if ( bOldMirroredX )
1961 nX = ( rInteraction.aPosition.X - aOld.Right() );
1962 if ( rNewRect.Left() > rNewRect.Right() )
1963 nX = maRect.Left() - nX;
1964 else
1965 nX += maRect.Right();
1967 else
1969 nX = ( rInteraction.aPosition.X - aOld.Left() );
1970 if ( rNewRect.Left() > rNewRect.Right() )
1971 nX = maRect.Right() - nX;
1972 else
1973 nX += maRect.Left();
1975 rInteraction.xInteraction->setControllerPosition( css::awt::Point( nX, rInteraction.xInteraction->getPosition().Y ) );
1977 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
1979 sal_Int32 nY;
1980 if ( bOldMirroredY )
1982 nY = ( rInteraction.aPosition.Y - aOld.Bottom() );
1983 if ( rNewRect.Top() > rNewRect.Bottom() )
1984 nY = maRect.Top() - nY;
1985 else
1986 nY += maRect.Bottom();
1988 else
1990 nY = ( rInteraction.aPosition.Y - aOld.Top() );
1991 if ( rNewRect.Top() > rNewRect.Bottom() )
1992 nY = maRect.Bottom() - nY;
1993 else
1994 nY += maRect.Top();
1996 rInteraction.xInteraction->setControllerPosition( css::awt::Point( rInteraction.xInteraction->getPosition().X, nY ) );
1999 catch ( const uno::RuntimeException& )
2005 void SdrObjCustomShape::DragMoveCustomShapeHdl( const Point& rDestination,
2006 const sal_uInt16 nCustomShapeHdlNum, bool bMoveCalloutRectangle )
2008 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
2009 if ( nCustomShapeHdlNum >= aInteractionHandles.size() )
2010 return;
2012 SdrCustomShapeInteraction aInteractionHandle( aInteractionHandles[ nCustomShapeHdlNum ] );
2013 if ( !aInteractionHandle.xInteraction.is() )
2014 return;
2018 css::awt::Point aPt( rDestination.X(), rDestination.Y() );
2019 if ( aInteractionHandle.nMode & CustomShapeHandleModes::MOVE_SHAPE && bMoveCalloutRectangle )
2021 sal_Int32 nXDiff = aPt.X - aInteractionHandle.aPosition.X;
2022 sal_Int32 nYDiff = aPt.Y - aInteractionHandle.aPosition.Y;
2024 maRect.Move( nXDiff, nYDiff );
2025 aOutRect.Move( nXDiff, nYDiff );
2026 maSnapRect.Move( nXDiff, nYDiff );
2027 SetRectsDirty(true);
2028 InvalidateRenderGeometry();
2030 for (const auto& rInteraction : aInteractionHandles)
2032 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
2034 if ( rInteraction.xInteraction.is() )
2035 rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
2039 aInteractionHandle.xInteraction->setControllerPosition( aPt );
2041 catch ( const uno::RuntimeException& )
2046 bool SdrObjCustomShape::applySpecialDrag(SdrDragStat& rDrag)
2048 const SdrHdl* pHdl = rDrag.GetHdl();
2049 const SdrHdlKind eHdl((pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind());
2051 switch(eHdl)
2053 case SdrHdlKind::CustomShape1 :
2055 rDrag.SetEndDragChangesGeoAndAttributes(true);
2056 DragMoveCustomShapeHdl( rDrag.GetNow(), static_cast<sal_uInt16>(pHdl->GetPointNum()), !rDrag.GetDragMethod()->IsShiftPressed() );
2057 SetRectsDirty();
2058 InvalidateRenderGeometry();
2059 SetChanged();
2060 break;
2063 case SdrHdlKind::UpperLeft :
2064 case SdrHdlKind::Upper :
2065 case SdrHdlKind::UpperRight :
2066 case SdrHdlKind::Left :
2067 case SdrHdlKind::Right :
2068 case SdrHdlKind::LowerLeft :
2069 case SdrHdlKind::Lower :
2070 case SdrHdlKind::LowerRight :
2072 DragResizeCustomShape( ImpDragCalcRect(rDrag) );
2073 break;
2075 case SdrHdlKind::Move :
2077 Move(Size(rDrag.GetDX(), rDrag.GetDY()));
2078 break;
2080 default: break;
2083 return true;
2087 void SdrObjCustomShape::DragCreateObject( SdrDragStat& rStat )
2089 tools::Rectangle aRect1;
2090 rStat.TakeCreateRect( aRect1 );
2092 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
2094 constexpr sal_uInt32 nDefaultObjectSizeWidth = 3000; // default width from SDOptions ?
2095 constexpr sal_uInt32 nDefaultObjectSizeHeight= 3000;
2097 if ( ImpVerticalSwitch( *this ) )
2099 SetMirroredX( aRect1.Left() > aRect1.Right() );
2101 aRect1 = tools::Rectangle( rStat.GetNow(), Size( nDefaultObjectSizeWidth, nDefaultObjectSizeHeight ) );
2102 // subtracting the horizontal difference of the latest handle from shape position
2103 if ( !aInteractionHandles.empty() )
2105 sal_Int32 nHandlePos = aInteractionHandles[ aInteractionHandles.size() - 1 ].xInteraction->getPosition().X;
2106 aRect1.Move( maRect.Left() - nHandlePos, 0 );
2109 ImpJustifyRect( aRect1 );
2110 rStat.SetActionRect( aRect1 );
2111 maRect = aRect1;
2112 SetRectsDirty();
2114 for (const auto& rInteraction : aInteractionHandles)
2118 if ( rInteraction.nMode & CustomShapeHandleModes::CREATE_FIXED )
2119 rInteraction.xInteraction->setControllerPosition( awt::Point( rStat.GetStart().X(), rStat.GetStart().Y() ) );
2121 catch ( const uno::RuntimeException& )
2126 SetBoundRectDirty();
2127 bSnapRectDirty=true;
2130 bool SdrObjCustomShape::MovCreate(SdrDragStat& rStat)
2132 SdrView* pView = rStat.GetView(); // #i37448#
2133 if( pView && pView->IsSolidDragging() )
2135 InvalidateRenderGeometry();
2137 DragCreateObject( rStat );
2138 SetRectsDirty();
2139 return true;
2142 bool SdrObjCustomShape::EndCreate( SdrDragStat& rStat, SdrCreateCmd eCmd )
2144 DragCreateObject( rStat );
2146 AdaptTextMinSize();
2148 SetRectsDirty();
2149 return ( eCmd == SdrCreateCmd::ForceEnd || rStat.GetPointCount() >= 2 );
2152 basegfx::B2DPolyPolygon SdrObjCustomShape::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
2154 return GetLineGeometry( false );
2158 // in context with the SdrObjCustomShape the SdrTextAutoGrowHeightItem == true -> Resize Shape to fit text,
2159 // the SdrTextAutoGrowWidthItem == true -> Word wrap text in Shape
2160 bool SdrObjCustomShape::IsAutoGrowHeight() const
2162 const SfxItemSet& rSet = GetMergedItemSet();
2163 bool bIsAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
2164 if ( bIsAutoGrowHeight && IsVerticalWriting() )
2165 bIsAutoGrowHeight = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue();
2166 return bIsAutoGrowHeight;
2168 bool SdrObjCustomShape::IsAutoGrowWidth() const
2170 const SfxItemSet& rSet = GetMergedItemSet();
2171 bool bIsAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
2172 if ( bIsAutoGrowWidth && !IsVerticalWriting() )
2173 bIsAutoGrowWidth = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue();
2174 return bIsAutoGrowWidth;
2177 /* The following method is identical to the SdrTextObj::SetVerticalWriting method, the only difference
2178 is that the SdrAutoGrowWidthItem and SdrAutoGrowHeightItem are not exchanged if the vertical writing
2179 mode has been changed */
2181 void SdrObjCustomShape::SetVerticalWriting( bool bVertical )
2183 ForceOutlinerParaObject();
2185 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
2187 DBG_ASSERT( pOutlinerParaObject, "SdrTextObj::SetVerticalWriting() without OutlinerParaObject!" );
2189 if( !pOutlinerParaObject ||
2190 (pOutlinerParaObject->IsVertical() == bVertical) )
2191 return;
2193 // get item settings
2194 const SfxItemSet& rSet = GetObjectItemSet();
2196 // Also exchange horizontal and vertical adjust items
2197 SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
2198 SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
2200 // rescue object size, SetSnapRect below expects logic rect,
2201 // not snap rect.
2202 tools::Rectangle aObjectRect = GetLogicRect();
2204 // prepare ItemSet to set exchanged width and height items
2205 SfxItemSet aNewSet(*rSet.GetPool(),
2206 svl::Items<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
2207 // Expanded item ranges to also support horizontal and vertical adjust.
2208 SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
2209 SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST>{});
2211 aNewSet.Put(rSet);
2213 // Exchange horizontal and vertical adjusts
2214 switch(eVert)
2216 case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
2217 case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
2218 case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
2219 case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
2221 switch(eHorz)
2223 case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
2224 case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
2225 case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
2226 case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
2229 pOutlinerParaObject = GetOutlinerParaObject();
2230 if ( pOutlinerParaObject )
2231 pOutlinerParaObject->SetVertical(bVertical);
2232 SetObjectItemSet( aNewSet );
2234 // restore object size
2235 SetSnapRect(aObjectRect);
2238 void SdrObjCustomShape::SuggestTextFrameSize(Size aSuggestedTextFrameSize)
2240 m_aSuggestedTextFrameSize = aSuggestedTextFrameSize;
2243 bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight(tools::Rectangle& rR, bool bHgt, bool bWdt) const
2245 // Either we have text or the application has native text and suggested its size to us.
2246 bool bHasText = HasText() || !m_aSuggestedTextFrameSize.IsEmpty();
2247 if ( bHasText && !rR.IsEmpty() )
2249 bool bWdtGrow=bWdt && IsAutoGrowWidth();
2250 bool bHgtGrow=bHgt && IsAutoGrowHeight();
2251 if ( bWdtGrow || bHgtGrow )
2253 tools::Rectangle aR0(rR);
2254 tools::Long nHgt=0,nMinHgt=0,nMaxHgt=0;
2255 tools::Long nWdt=0,nMinWdt=0,nMaxWdt=0;
2256 Size aSiz(rR.GetSize()); aSiz.AdjustWidth( -1 ); aSiz.AdjustHeight( -1 );
2257 Size aMaxSiz(100000,100000);
2258 Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
2259 if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
2260 if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
2261 if (bWdtGrow)
2263 nMinWdt=GetMinTextFrameWidth();
2264 nMaxWdt=GetMaxTextFrameWidth();
2265 if (nMaxWdt==0 || nMaxWdt>aMaxSiz.Width()) nMaxWdt=aMaxSiz.Width();
2266 if (nMinWdt<=0) nMinWdt=1;
2267 aSiz.setWidth(nMaxWdt );
2269 if (bHgtGrow)
2271 nMinHgt=GetMinTextFrameHeight();
2272 nMaxHgt=GetMaxTextFrameHeight();
2273 if (nMaxHgt==0 || nMaxHgt>aMaxSiz.Height()) nMaxHgt=aMaxSiz.Height();
2274 if (nMinHgt<=0) nMinHgt=1;
2275 aSiz.setHeight(nMaxHgt );
2277 tools::Long nHDist=GetTextLeftDistance()+GetTextRightDistance();
2278 tools::Long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
2279 aSiz.AdjustWidth( -nHDist );
2280 aSiz.AdjustHeight( -nVDist );
2281 if ( aSiz.Width() < 2 )
2282 aSiz.setWidth( 2 ); // minimum size=2
2283 if ( aSiz.Height() < 2 )
2284 aSiz.setHeight( 2 ); // minimum size=2
2286 if (HasText())
2288 if(pEdtOutl)
2290 pEdtOutl->SetMaxAutoPaperSize( aSiz );
2291 if (bWdtGrow)
2293 Size aSiz2(pEdtOutl->CalcTextSize());
2294 nWdt=aSiz2.Width()+1; // a little more tolerance
2295 if (bHgtGrow) nHgt=aSiz2.Height()+1; // a little more tolerance
2296 } else
2298 nHgt=pEdtOutl->GetTextHeight()+1; // a little more tolerance
2301 else
2303 Outliner& rOutliner=ImpGetDrawOutliner();
2304 rOutliner.SetPaperSize(aSiz);
2305 rOutliner.SetUpdateMode(true);
2306 // TODO: add the optimization with bPortionInfoChecked again.
2307 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
2308 if( pOutlinerParaObject != nullptr )
2310 rOutliner.SetText(*pOutlinerParaObject);
2311 rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
2313 if ( bWdtGrow )
2315 Size aSiz2(rOutliner.CalcTextSize());
2316 nWdt=aSiz2.Width()+1; // a little more tolerance
2317 if ( bHgtGrow )
2318 nHgt=aSiz2.Height()+1; // a little more tolerance
2320 else
2321 nHgt = rOutliner.GetTextHeight()+1; // a little more tolerance
2322 rOutliner.Clear();
2325 else
2327 nHgt = m_aSuggestedTextFrameSize.Height();
2328 nWdt = m_aSuggestedTextFrameSize.Width();
2330 if ( nWdt < nMinWdt )
2331 nWdt = nMinWdt;
2332 if ( nWdt > nMaxWdt )
2333 nWdt = nMaxWdt;
2334 nWdt += nHDist;
2335 if ( nWdt < 1 )
2336 nWdt = 1; // nHDist may also be negative
2337 if ( nHgt < nMinHgt )
2338 nHgt = nMinHgt;
2339 if ( nHgt > nMaxHgt )
2340 nHgt = nMaxHgt;
2341 nHgt+=nVDist;
2342 if ( nHgt < 1 )
2343 nHgt = 1; // nVDist may also be negative
2344 tools::Long nWdtGrow = nWdt-(rR.Right()-rR.Left());
2345 tools::Long nHgtGrow = nHgt-(rR.Bottom()-rR.Top());
2346 if ( nWdtGrow == 0 )
2347 bWdtGrow = false;
2348 if ( nHgtGrow == 0 )
2349 bHgtGrow=false;
2350 if ( bWdtGrow || bHgtGrow || !m_aSuggestedTextFrameSize.IsEmpty())
2352 if ( bWdtGrow || m_aSuggestedTextFrameSize.Width() )
2354 SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
2355 if (m_aSuggestedTextFrameSize.Width())
2357 rR.SetRight(rR.Left() + m_aSuggestedTextFrameSize.Width());
2359 else if ( eHAdj == SDRTEXTHORZADJUST_LEFT )
2360 rR.AdjustRight(nWdtGrow );
2361 else if ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
2362 rR.AdjustLeft( -nWdtGrow );
2363 else
2365 tools::Long nWdtGrow2=nWdtGrow/2;
2366 rR.AdjustLeft( -nWdtGrow2 );
2367 rR.SetRight(rR.Left()+nWdt );
2370 if ( bHgtGrow || m_aSuggestedTextFrameSize.Height() )
2372 SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
2373 if (m_aSuggestedTextFrameSize.Height())
2375 rR.SetBottom(rR.Top() + m_aSuggestedTextFrameSize.Height());
2377 else if ( eVAdj == SDRTEXTVERTADJUST_TOP )
2378 rR.AdjustBottom(nHgtGrow );
2379 else if ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
2380 rR.AdjustTop( -nHgtGrow );
2381 else
2383 tools::Long nHgtGrow2=nHgtGrow/2;
2384 rR.AdjustTop( -nHgtGrow2 );
2385 rR.SetBottom(rR.Top()+nHgt );
2388 if ( aGeo.nRotationAngle )
2390 Point aD1(rR.TopLeft());
2391 aD1-=aR0.TopLeft();
2392 Point aD2(aD1);
2393 RotatePoint(aD2,Point(),aGeo.nSin,aGeo.nCos);
2394 aD2-=aD1;
2395 rR.Move(aD2.X(),aD2.Y());
2397 return true;
2401 return false;
2404 tools::Rectangle SdrObjCustomShape::ImpCalculateTextFrame( const bool bHgt, const bool bWdt )
2406 tools::Rectangle aReturnValue;
2408 tools::Rectangle aOldTextRect( maRect ); // <- initial text rectangle
2410 tools::Rectangle aNewTextRect( maRect ); // <- new text rectangle returned from the custom shape renderer,
2411 GetTextBounds( aNewTextRect ); // it depends to the current logical shape size
2413 tools::Rectangle aAdjustedTextRect( aNewTextRect ); // <- new text rectangle is being tested by AdjustTextFrameWidthAndHeight to ensure
2414 if ( AdjustTextFrameWidthAndHeight( aAdjustedTextRect, bHgt, bWdt ) ) // that the new text rectangle is matching the current text size from the outliner
2416 if (aAdjustedTextRect != aNewTextRect && aOldTextRect != aAdjustedTextRect &&
2417 aNewTextRect.GetWidth() && aNewTextRect.GetHeight())
2419 aReturnValue = maRect;
2420 double fXScale = static_cast<double>(aOldTextRect.GetWidth()) / static_cast<double>(aNewTextRect.GetWidth());
2421 double fYScale = static_cast<double>(aOldTextRect.GetHeight()) / static_cast<double>(aNewTextRect.GetHeight());
2422 double fRightDiff = static_cast<double>( aAdjustedTextRect.Right() - aNewTextRect.Right() ) * fXScale;
2423 double fLeftDiff = static_cast<double>( aAdjustedTextRect.Left() - aNewTextRect.Left() ) * fXScale;
2424 double fTopDiff = static_cast<double>( aAdjustedTextRect.Top() - aNewTextRect.Top() ) * fYScale;
2425 double fBottomDiff= static_cast<double>( aAdjustedTextRect.Bottom()- aNewTextRect.Bottom()) * fYScale;
2426 aReturnValue.AdjustLeft(static_cast<sal_Int32>(fLeftDiff) );
2427 aReturnValue.AdjustRight(static_cast<sal_Int32>(fRightDiff) );
2428 aReturnValue.AdjustTop(static_cast<sal_Int32>(fTopDiff) );
2429 aReturnValue.AdjustBottom(static_cast<sal_Int32>(fBottomDiff) );
2432 return aReturnValue;
2435 bool SdrObjCustomShape::NbcAdjustTextFrameWidthAndHeight(bool bHgt, bool bWdt)
2437 tools::Rectangle aNewTextRect = ImpCalculateTextFrame(bHgt, bWdt);
2438 const bool bRet = !aNewTextRect.IsEmpty() && aNewTextRect != maRect;
2439 if (bRet && !mbAdjustingTextFrameWidthAndHeight)
2441 mbAdjustingTextFrameWidthAndHeight = true;
2443 // taking care of handles that should not been changed
2444 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
2446 maRect = aNewTextRect;
2447 SetRectsDirty();
2448 SetChanged();
2450 for (const auto& rInteraction : aInteractionHandles)
2454 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
2455 rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
2457 catch ( const uno::RuntimeException& )
2461 InvalidateRenderGeometry();
2463 mbAdjustingTextFrameWidthAndHeight = false;
2465 return bRet;
2468 bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight()
2470 tools::Rectangle aNewTextRect = ImpCalculateTextFrame( true/*bHgt*/, true/*bWdt*/ );
2471 bool bRet = !aNewTextRect.IsEmpty() && ( aNewTextRect != maRect );
2472 if ( bRet )
2474 tools::Rectangle aBoundRect0;
2475 if ( pUserCall )
2476 aBoundRect0 = GetCurrentBoundRect();
2478 // taking care of handles that should not been changed
2479 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
2481 maRect = aNewTextRect;
2482 SetRectsDirty();
2484 for (const auto& rInteraction : aInteractionHandles)
2488 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
2489 rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
2491 catch ( const uno::RuntimeException& )
2496 InvalidateRenderGeometry();
2497 SetChanged();
2498 BroadcastObjectChange();
2499 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
2501 return bRet;
2503 void SdrObjCustomShape::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
2505 Size aPaperMin,aPaperMax;
2506 tools::Rectangle aViewInit;
2507 TakeTextAnchorRect( aViewInit );
2508 if ( aGeo.nRotationAngle )
2510 Point aCenter(aViewInit.Center());
2511 aCenter-=aViewInit.TopLeft();
2512 Point aCenter0(aCenter);
2513 RotatePoint(aCenter,Point(),aGeo.nSin,aGeo.nCos);
2514 aCenter-=aCenter0;
2515 aViewInit.Move(aCenter.X(),aCenter.Y());
2517 Size aAnkSiz(aViewInit.GetSize());
2518 aAnkSiz.AdjustWidth( -1 ); aAnkSiz.AdjustHeight( -1 ); // because GetSize() adds 1
2519 Size aMaxSiz(1000000,1000000);
2521 Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
2522 if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
2523 if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
2525 SdrTextHorzAdjust eHAdj(GetTextHorizontalAdjust());
2526 SdrTextVertAdjust eVAdj(GetTextVerticalAdjust());
2528 tools::Long nMinWdt = GetMinTextFrameWidth();
2529 tools::Long nMinHgt = GetMinTextFrameHeight();
2530 tools::Long nMaxWdt = GetMaxTextFrameWidth();
2531 tools::Long nMaxHgt = GetMaxTextFrameHeight();
2532 if (nMinWdt<1) nMinWdt=1;
2533 if (nMinHgt<1) nMinHgt=1;
2534 if ( nMaxWdt == 0 || nMaxWdt > aMaxSiz.Width() )
2535 nMaxWdt = aMaxSiz.Width();
2536 if ( nMaxHgt == 0 || nMaxHgt > aMaxSiz.Height() )
2537 nMaxHgt=aMaxSiz.Height();
2539 if (GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue())
2541 if ( IsVerticalWriting() )
2543 nMaxHgt = aAnkSiz.Height();
2544 nMinHgt = nMaxHgt;
2546 else
2548 nMaxWdt = aAnkSiz.Width();
2549 nMinWdt = nMaxWdt;
2552 aPaperMax.setWidth(nMaxWdt );
2553 aPaperMax.setHeight(nMaxHgt );
2555 aPaperMin.setWidth(nMinWdt );
2556 aPaperMin.setHeight(nMinHgt );
2558 if ( pViewMin )
2560 *pViewMin = aViewInit;
2562 tools::Long nXFree = aAnkSiz.Width() - aPaperMin.Width();
2563 if ( eHAdj == SDRTEXTHORZADJUST_LEFT )
2564 pViewMin->AdjustRight( -nXFree );
2565 else if ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
2566 pViewMin->AdjustLeft(nXFree );
2567 else { pViewMin->AdjustLeft(nXFree / 2 ); pViewMin->SetRight( pViewMin->Left() + aPaperMin.Width() ); }
2569 tools::Long nYFree = aAnkSiz.Height() - aPaperMin.Height();
2570 if ( eVAdj == SDRTEXTVERTADJUST_TOP )
2571 pViewMin->AdjustBottom( -nYFree );
2572 else if ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
2573 pViewMin->AdjustTop(nYFree );
2574 else { pViewMin->AdjustTop(nYFree / 2 ); pViewMin->SetBottom( pViewMin->Top() + aPaperMin.Height() ); }
2577 if( IsVerticalWriting() )
2578 aPaperMin.setWidth( 0 );
2579 else
2580 aPaperMin.setHeight( 0 );
2582 if( eHAdj != SDRTEXTHORZADJUST_BLOCK )
2583 aPaperMin.setWidth(0 );
2585 // For complete vertical adjust support, set paper min height to 0, here.
2586 if(SDRTEXTVERTADJUST_BLOCK != eVAdj )
2587 aPaperMin.setHeight( 0 );
2589 if (pPaperMin!=nullptr) *pPaperMin=aPaperMin;
2590 if (pPaperMax!=nullptr) *pPaperMax=aPaperMax;
2591 if (pViewInit!=nullptr) *pViewInit=aViewInit;
2593 void SdrObjCustomShape::EndTextEdit( SdrOutliner& rOutl )
2595 SdrTextObj::EndTextEdit( rOutl );
2596 InvalidateRenderGeometry();
2598 void SdrObjCustomShape::TakeTextAnchorRect( tools::Rectangle& rAnchorRect ) const
2600 if ( GetTextBounds( rAnchorRect ) )
2602 Point aRotateRef( maSnapRect.Center() );
2603 rAnchorRect.AdjustLeft(GetTextLeftDistance() );
2604 rAnchorRect.AdjustTop(GetTextUpperDistance() );
2605 rAnchorRect.AdjustRight( -(GetTextRightDistance()) );
2606 rAnchorRect.AdjustBottom( -(GetTextLowerDistance()) );
2607 ImpJustifyRect( rAnchorRect );
2609 if ( rAnchorRect.GetWidth() < 2 )
2610 rAnchorRect.SetRight( rAnchorRect.Left() + 1 ); // minimal width is 2
2611 if ( rAnchorRect.GetHeight() < 2 )
2612 rAnchorRect.SetBottom( rAnchorRect.Top() + 1 ); // minimal height is 2
2613 if ( aGeo.nRotationAngle )
2615 Point aP( rAnchorRect.TopLeft() );
2616 RotatePoint( aP, aRotateRef, aGeo.nSin, aGeo. nCos );
2617 rAnchorRect.SetPos( aP );
2620 else
2621 SdrTextObj::TakeTextAnchorRect( rAnchorRect );
2623 void SdrObjCustomShape::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
2624 tools::Rectangle* pAnchorRect, bool /*bLineWidth*/) const
2626 tools::Rectangle aAnkRect; // Rect in which we anchor
2627 TakeTextAnchorRect(aAnkRect);
2628 SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
2629 SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
2630 EEControlBits nStat0=rOutliner.GetControlWord();
2631 Size aNullSize;
2633 rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
2634 rOutliner.SetMinAutoPaperSize(aNullSize);
2635 sal_Int32 nMaxAutoPaperWidth = 1000000;
2636 sal_Int32 nMaxAutoPaperHeight= 1000000;
2638 tools::Long nAnkWdt=aAnkRect.GetWidth();
2639 tools::Long nAnkHgt=aAnkRect.GetHeight();
2641 if (GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue())
2643 if ( IsVerticalWriting() )
2644 nMaxAutoPaperHeight = nAnkHgt;
2645 else
2646 nMaxAutoPaperWidth = nAnkWdt;
2648 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
2650 rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
2653 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
2655 rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
2657 rOutliner.SetMaxAutoPaperSize( Size( nMaxAutoPaperWidth, nMaxAutoPaperHeight ) );
2658 rOutliner.SetPaperSize( aNullSize );
2660 // put text into the Outliner - if necessary the use the text from the EditOutliner
2661 OutlinerParaObject* pPara= GetOutlinerParaObject();
2662 if (pEdtOutl && !bNoEditText)
2663 pPara=pEdtOutl->CreateParaObject().release();
2665 if (pPara)
2667 bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
2668 const SdrTextObj* pTestObj = rOutliner.GetTextObj();
2670 if( !pTestObj || !bHitTest || pTestObj != this ||
2671 pTestObj->GetOutlinerParaObject() != GetOutlinerParaObject() )
2673 if( bHitTest )
2674 rOutliner.SetTextObj( this );
2676 rOutliner.SetUpdateMode(true);
2677 rOutliner.SetText(*pPara);
2680 else
2682 rOutliner.SetTextObj( nullptr );
2684 if (pEdtOutl && !bNoEditText && pPara)
2685 delete pPara;
2687 rOutliner.SetUpdateMode(true);
2688 rOutliner.SetControlWord(nStat0);
2690 SdrText* pText = getActiveText();
2691 if( pText )
2692 pText->CheckPortionInfo( rOutliner );
2694 Point aTextPos(aAnkRect.TopLeft());
2695 Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() has a little added tolerance, no?
2697 // For draw objects containing text correct horizontal/vertical alignment if text is bigger
2698 // than the object itself. Without that correction, the text would always be
2699 // formatted to the left edge (or top edge when vertical) of the draw object.
2701 if( !IsTextFrame() )
2703 if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
2705 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
2706 // else the alignment is wanted.
2707 if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
2709 SvxAdjust eAdjust = GetObjectItemSet().Get(EE_PARA_JUST).GetAdjust();
2710 switch (eAdjust)
2712 case SvxAdjust::Left: eHAdj = SDRTEXTHORZADJUST_LEFT; break;
2713 case SvxAdjust::Right: eHAdj = SDRTEXTHORZADJUST_RIGHT; break;
2714 case SvxAdjust::Center: eHAdj = SDRTEXTHORZADJUST_CENTER; break;
2715 default: break;
2720 if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
2722 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
2723 // else the alignment is wanted.
2724 if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
2726 eVAdj = SDRTEXTVERTADJUST_CENTER;
2731 if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
2733 tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
2734 if (eHAdj==SDRTEXTHORZADJUST_CENTER)
2735 aTextPos.AdjustX(nFreeWdt/2 );
2736 if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
2737 aTextPos.AdjustX(nFreeWdt );
2739 if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
2741 tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
2742 if (eVAdj==SDRTEXTVERTADJUST_CENTER)
2743 aTextPos.AdjustY(nFreeHgt/2 );
2744 if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
2745 aTextPos.AdjustY(nFreeHgt );
2747 if (aGeo.nRotationAngle!=0)
2748 RotatePoint(aTextPos,aAnkRect.TopLeft(),aGeo.nSin,aGeo.nCos);
2750 if (pAnchorRect)
2751 *pAnchorRect=aAnkRect;
2753 // using rTextRect together with ContourFrame doesn't always work correctly
2754 rTextRect=tools::Rectangle(aTextPos,aTextSiz);
2757 void SdrObjCustomShape::NbcSetOutlinerParaObject(std::unique_ptr<OutlinerParaObject> pTextObject)
2759 SdrTextObj::NbcSetOutlinerParaObject( std::move(pTextObject) );
2760 SetBoundRectDirty();
2761 SetRectsDirty(true);
2762 InvalidateRenderGeometry();
2765 SdrObjCustomShape* SdrObjCustomShape::CloneSdrObject(SdrModel& rTargetModel) const
2767 return CloneHelper< SdrObjCustomShape >(rTargetModel);
2770 SdrObjCustomShape& SdrObjCustomShape::operator=(const SdrObjCustomShape& rObj)
2772 if( this == &rObj )
2773 return *this;
2774 SdrTextObj::operator=( rObj );
2775 fObjectRotation = rObj.fObjectRotation;
2776 mbAdjustingTextFrameWidthAndHeight = rObj.mbAdjustingTextFrameWidthAndHeight;
2777 assert(!mbAdjustingTextFrameWidthAndHeight);
2778 InvalidateRenderGeometry();
2779 return *this;
2783 OUString SdrObjCustomShape::TakeObjNameSingul() const
2785 OUStringBuffer sName(SvxResId(STR_ObjNameSingulCUSTOMSHAPE));
2786 OUString aNm(GetName());
2787 if (!aNm.isEmpty())
2789 sName.append(' ');
2790 sName.append('\'');
2791 sName.append(aNm);
2792 sName.append('\'');
2794 return sName.makeStringAndClear();
2797 OUString SdrObjCustomShape::TakeObjNamePlural() const
2799 return SvxResId(STR_ObjNamePluralCUSTOMSHAPE);
2802 basegfx::B2DPolyPolygon SdrObjCustomShape::TakeXorPoly() const
2804 return GetLineGeometry( false );
2807 basegfx::B2DPolyPolygon SdrObjCustomShape::TakeContour() const
2809 const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
2810 if ( pSdrObject )
2811 return pSdrObject->TakeContour();
2812 return basegfx::B2DPolyPolygon();
2815 SdrObjectUniquePtr SdrObjCustomShape::DoConvertToPolyObj(bool bBezier, bool bAddText) const
2817 // #i37011#
2818 SdrObjectUniquePtr pRetval;
2819 SdrObject* pRenderedCustomShape = nullptr;
2821 if ( !mXRenderedCustomShape.is() )
2823 // force CustomShape
2824 GetSdrObjectFromCustomShape();
2827 if ( mXRenderedCustomShape.is() )
2829 pRenderedCustomShape = GetSdrObjectFromXShape( mXRenderedCustomShape );
2832 if ( pRenderedCustomShape )
2834 // Clone to same SdrModel
2835 SdrObject* pCandidate(pRenderedCustomShape->CloneSdrObject(pRenderedCustomShape->getSdrModelFromSdrObject()));
2836 DBG_ASSERT(pCandidate, "SdrObjCustomShape::DoConvertToPolyObj: Could not clone SdrObject (!)");
2837 pRetval = pCandidate->DoConvertToPolyObj(bBezier, bAddText);
2838 SdrObject::Free( pCandidate );
2840 if(pRetval)
2842 const bool bShadow(GetMergedItem(SDRATTR_SHADOW).GetValue());
2843 if(bShadow)
2845 pRetval->SetMergedItem(makeSdrShadowItem(true));
2849 if(bAddText && HasText() && !IsTextPath())
2851 pRetval = ImpConvertAddText(std::move(pRetval), bBezier);
2855 return pRetval;
2858 void SdrObjCustomShape::NbcSetStyleSheet( SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr )
2860 // #i40944#
2861 InvalidateRenderGeometry();
2862 SdrObject::NbcSetStyleSheet( pNewStyleSheet, bDontRemoveHardAttr );
2865 void SdrObjCustomShape::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
2867 // call parent
2868 SdrTextObj::handlePageChange(pOldPage, pNewPage);
2870 if(nullptr != pNewPage)
2872 // invalidating rectangles by SetRectsDirty is not sufficient,
2873 // AdjustTextFrameWidthAndHeight() also has to be made, both
2874 // actions are done by NbcSetSnapRect
2875 tools::Rectangle aTmp( maRect ); //creating temporary rectangle #i61108#
2876 NbcSetSnapRect( aTmp );
2880 SdrObjGeoData* SdrObjCustomShape::NewGeoData() const
2882 return new SdrAShapeObjGeoData;
2885 void SdrObjCustomShape::SaveGeoData(SdrObjGeoData& rGeo) const
2887 SdrTextObj::SaveGeoData( rGeo );
2888 SdrAShapeObjGeoData& rAGeo=static_cast<SdrAShapeObjGeoData&>(rGeo);
2889 rAGeo.fObjectRotation = fObjectRotation;
2890 rAGeo.bMirroredX = IsMirroredX();
2891 rAGeo.bMirroredY = IsMirroredY();
2893 const Any* pAny = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ).GetPropertyValueByName( "AdjustmentValues" );
2894 if ( pAny )
2895 *pAny >>= rAGeo.aAdjustmentSeq;
2898 void SdrObjCustomShape::RestGeoData(const SdrObjGeoData& rGeo)
2900 SdrTextObj::RestGeoData( rGeo );
2901 const SdrAShapeObjGeoData& rAGeo=static_cast<const SdrAShapeObjGeoData&>(rGeo);
2902 fObjectRotation = rAGeo.fObjectRotation;
2903 SetMirroredX( rAGeo.bMirroredX );
2904 SetMirroredY( rAGeo.bMirroredY );
2906 SdrCustomShapeGeometryItem rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
2907 PropertyValue aPropVal;
2908 aPropVal.Name = "AdjustmentValues";
2909 aPropVal.Value <<= rAGeo.aAdjustmentSeq;
2910 rGeometryItem.SetPropertyValue( aPropVal );
2911 SetMergedItem( rGeometryItem );
2913 InvalidateRenderGeometry();
2916 void SdrObjCustomShape::AdjustToMaxRect(const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */)
2918 SAL_INFO_IF(bShrinkOnly, "svx", "Case bShrinkOnly == true is not implemented yet.");
2920 if (rMaxRect.IsEmpty() || rMaxRect == GetSnapRect())
2921 return;
2923 // Get a matrix, that would produce the existing shape, when applied to a unit square
2924 basegfx::B2DPolyPolygon aPolyPolygon; //not used, but formal needed
2925 basegfx::B2DHomMatrix aMatrix;
2926 TRGetBaseGeometry(aMatrix, aPolyPolygon);
2927 // Using TRSetBaseGeometry(aMatrix, aPolyPolygon) would regenerate the current shape. But
2928 // applying aMatrix to a unit square will not generate the current shape. Scaling,
2929 // rotation and translation are correct, but shear angle has wrong sign. So break up
2930 // matrix and create a mathematically correct new one.
2931 basegfx::B2DTuple aScale;
2932 basegfx::B2DTuple aTranslate;
2933 double fRotate, fShearX;
2934 aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
2935 basegfx::B2DHomMatrix aMathMatrix;
2936 aMathMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
2937 aScale,
2938 basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
2939 basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
2940 aTranslate);
2942 // Calculate scaling factors from size of the transformed unit polygon as ersatz for the not
2943 // usable current snap rectangle.
2944 basegfx::B2DPolygon aB2DPolygon(basegfx::utils::createUnitPolygon());
2945 aB2DPolygon.transform(aMathMatrix);
2946 basegfx::B2DRange aB2DRange(aB2DPolygon.getB2DRange());
2947 double fPolygonWidth = aB2DRange.getWidth();
2948 if (fPolygonWidth == 0)
2949 fPolygonWidth = 1;
2950 double fPolygonHeight = aB2DRange.getHeight();
2951 if (fPolygonHeight == 0)
2952 fPolygonHeight = 1;
2953 const double aFactorX = static_cast<double>(rMaxRect.GetWidth()) / fPolygonWidth;
2954 const double aFactorY = static_cast<double>(rMaxRect.GetHeight()) / fPolygonHeight;
2956 // Generate matrix, that would produce the desired rMaxRect when applied to unit square
2957 aMathMatrix.scale(aFactorX, aFactorY);
2958 aB2DPolygon = basegfx::utils::createUnitPolygon();
2959 aB2DPolygon.transform(aMathMatrix);
2960 aB2DRange = aB2DPolygon.getB2DRange();
2961 const double fPolygonLeft = aB2DRange.getMinX();
2962 const double fPolygonTop = aB2DRange.getMinY();
2963 aMathMatrix.translate(rMaxRect.getX() - fPolygonLeft, rMaxRect.getY() - fPolygonTop);
2965 // Create a Matrix from aMathMatrix, which is usable with TRSetBaseGeometry
2966 aMathMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
2967 aMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
2968 aScale,
2969 basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
2970 basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
2971 aTranslate);
2973 // Now use TRSetBaseGeometry to actually perform scale, shear, rotate and translate
2974 // on the shape. That considers gluepoints, interaction handles and text area, and includes
2975 // setting rectangles dirty and broadcast.
2976 TRSetBaseGeometry(aMatrix, aPolyPolygon);
2979 void SdrObjCustomShape::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
2981 // The shape might have already flipping in its enhanced geometry. LibreOffice applies
2982 // such after all transformations. We remove it, but remember it to apply them later.
2983 bool bIsMirroredX = IsMirroredX();
2984 bool bIsMirroredY = IsMirroredY();
2985 if (bIsMirroredX || bIsMirroredY)
2987 Point aCurrentCenter = GetSnapRect().Center();
2988 if (bIsMirroredX) // mirror on the y-axis
2990 Mirror(aCurrentCenter, Point(aCurrentCenter.X(), aCurrentCenter.Y() + 1000));
2992 if (bIsMirroredY) // mirror on the x-axis
2994 Mirror(aCurrentCenter, Point(aCurrentCenter.X() + 1000, aCurrentCenter.Y()));
2998 // break up matrix
2999 basegfx::B2DTuple aScale;
3000 basegfx::B2DTuple aTranslate;
3001 double fRotate, fShearX;
3002 rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
3004 // reset object shear and rotations
3005 fObjectRotation = 0.0;
3006 aGeo.nRotationAngle = 0;
3007 aGeo.RecalcSinCos();
3008 aGeo.nShearAngle = 0;
3009 aGeo.RecalcTan();
3011 // if anchor is used, make position relative to it
3012 if(getSdrModelFromSdrObject().IsWriter())
3014 if(GetAnchorPos().X() || GetAnchorPos().Y())
3016 aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
3020 // scale
3021 Size aSize(FRound(fabs(aScale.getX())), FRound(fabs(aScale.getY())));
3022 // fdo#47434 We need a valid rectangle here
3023 if( !aSize.Height() ) aSize.setHeight( 1 );
3024 if( !aSize.Width() ) aSize.setWidth( 1 );
3025 tools::Rectangle aBaseRect(Point(), aSize);
3026 SetLogicRect(aBaseRect);
3028 // Apply flipping from Matrix, which is a transformation relative to origin
3029 if (basegfx::fTools::less(aScale.getX(), 0.0))
3030 Mirror(Point(0, 0), Point(0, 1000)); // mirror on the y-axis
3031 if (basegfx::fTools::less(aScale.getY(), 0.0))
3032 Mirror(Point(0, 0), Point(1000, 0)); // mirror on the x-axis
3034 // shear?
3035 if(!basegfx::fTools::equalZero(fShearX))
3037 GeoStat aGeoStat;
3038 // #i123181# The fix for #121932# here was wrong, the trunk version does not correct the
3039 // mirrored shear values, neither at the object level, nor on the API or XML level. Taking
3040 // back the mirroring of the shear angle
3041 aGeoStat.nShearAngle = FRound(basegfx::rad2deg(atan(fShearX)) * 100.0);
3042 aGeoStat.RecalcTan();
3043 Shear(Point(), aGeoStat.nShearAngle, aGeoStat.nTan, false);
3046 // rotation?
3047 if(!basegfx::fTools::equalZero(fRotate))
3049 GeoStat aGeoStat;
3051 // #i78696#
3052 // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
3053 // mirrored -> mirror value here
3054 aGeoStat.nRotationAngle = NormAngle36000(FRound(-fRotate / F_PI18000));
3055 aGeoStat.RecalcSinCos();
3056 Rotate(Point(), aGeoStat.nRotationAngle, aGeoStat.nSin, aGeoStat.nCos);
3059 // translate?
3060 if(!aTranslate.equalZero())
3062 Move(Size(FRound(aTranslate.getX()), FRound(aTranslate.getY())));
3065 // Apply flipping from enhanced geometry at center of the shape.
3066 if (!(bIsMirroredX || bIsMirroredY))
3067 return;
3069 // create mathematically matrix for the applied transformations
3070 // aScale was in most cases built from a rectangle including edge
3071 // and is therefore mathematically too large by 1
3072 if (aScale.getX() > 2.0 && aScale.getY() > 2.0)
3073 aScale -= basegfx::B2DTuple(1.0, 1.0);
3074 basegfx::B2DHomMatrix aMathMat = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
3075 aScale, -fShearX, basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
3076 aTranslate);
3077 // Use matrix to get current center
3078 basegfx::B2DPoint aCenter(0.5,0.5);
3079 aCenter = aMathMat * aCenter;
3080 double fCenterX = aCenter.getX();
3081 double fCenterY = aCenter.getY();
3082 if (bIsMirroredX) // vertical axis
3083 Mirror(Point(FRound(fCenterX),FRound(fCenterY)),
3084 Point(FRound(fCenterX), FRound(fCenterY + 1000.0)));
3085 if (bIsMirroredY) // horizontal axis
3086 Mirror(Point(FRound(fCenterX),FRound(fCenterY)),
3087 Point(FRound(fCenterX + 1000.0), FRound(fCenterY)));
3090 // taking fObjectRotation instead of aGeo.nAngle
3091 bool SdrObjCustomShape::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
3093 // get turn and shear
3094 double fRotate = basegfx::deg2rad(fObjectRotation);
3095 double fShearX = basegfx::deg2rad(aGeo.nShearAngle / 100.0);
3097 // get aRect, this is the unrotated snaprect
3098 tools::Rectangle aRectangle(maRect);
3100 bool bMirroredX = IsMirroredX();
3101 bool bMirroredY = IsMirroredY();
3102 if ( bMirroredX || bMirroredY )
3103 { // we have to retrieve the unmirrored rect
3105 GeoStat aNewGeo( aGeo );
3107 if ( bMirroredX )
3109 fShearX = -fShearX;
3110 tools::Polygon aPol = Rect2Poly(maRect, aNewGeo);
3111 tools::Rectangle aBoundRect( aPol.GetBoundRect() );
3113 Point aRef1( ( aBoundRect.Left() + aBoundRect.Right() ) >> 1, aBoundRect.Top() );
3114 Point aRef2( aRef1.X(), aRef1.Y() + 1000 );
3115 sal_uInt16 i;
3116 sal_uInt16 nPointCount=aPol.GetSize();
3117 for (i=0; i<nPointCount; i++)
3119 MirrorPoint(aPol[i],aRef1,aRef2);
3121 // mirror polygon and move it a bit
3122 tools::Polygon aPol0(aPol);
3123 aPol[0]=aPol0[1];
3124 aPol[1]=aPol0[0];
3125 aPol[2]=aPol0[3];
3126 aPol[3]=aPol0[2];
3127 aPol[4]=aPol0[1];
3128 Poly2Rect(aPol,aRectangle,aNewGeo);
3130 if ( bMirroredY )
3132 fShearX = -fShearX;
3133 tools::Polygon aPol( Rect2Poly( aRectangle, aNewGeo ) );
3134 tools::Rectangle aBoundRect( aPol.GetBoundRect() );
3136 Point aRef1( aBoundRect.Left(), ( aBoundRect.Top() + aBoundRect.Bottom() ) >> 1 );
3137 Point aRef2( aRef1.X() + 1000, aRef1.Y() );
3138 sal_uInt16 i;
3139 sal_uInt16 nPointCount=aPol.GetSize();
3140 for (i=0; i<nPointCount; i++)
3142 MirrorPoint(aPol[i],aRef1,aRef2);
3144 // mirror polygon and move it a bit
3145 tools::Polygon aPol0(aPol);
3146 aPol[0]=aPol0[1]; // This was WRONG for vertical (!)
3147 aPol[1]=aPol0[0]; // #i121932# Despite my own comment above
3148 aPol[2]=aPol0[3]; // it was *not* wrong even when the reordering
3149 aPol[3]=aPol0[2]; // *seems* to be specific for X-Mirrorings. Oh
3150 aPol[4]=aPol0[1]; // will I be happy when this old stuff is |gone| with aw080 (!)
3151 Poly2Rect(aPol,aRectangle,aNewGeo);
3155 // fill other values
3156 basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
3157 basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
3159 // position may be relative to anchorpos, convert
3160 if(getSdrModelFromSdrObject().IsWriter())
3162 if(GetAnchorPos().X() || GetAnchorPos().Y())
3164 aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
3168 // build matrix
3169 rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
3170 aScale,
3171 basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
3172 basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
3173 aTranslate);
3175 return false;
3178 std::unique_ptr<sdr::contact::ViewContact> SdrObjCustomShape::CreateObjectSpecificViewContact()
3180 return std::make_unique<sdr::contact::ViewContactOfSdrObjCustomShape>(*this);
3183 // #i33136#
3184 bool SdrObjCustomShape::doConstructOrthogonal(const OUString& rName)
3186 bool bRetval(false);
3188 if(rName.equalsIgnoreAsciiCase("quadrat"))
3190 bRetval = true;
3192 else if(rName.equalsIgnoreAsciiCase("round-quadrat"))
3194 bRetval = true;
3196 else if(rName.equalsIgnoreAsciiCase("circle"))
3198 bRetval = true;
3200 else if(rName.equalsIgnoreAsciiCase("circle-pie"))
3202 bRetval = true;
3204 else if(rName.equalsIgnoreAsciiCase("ring"))
3206 bRetval = true;
3209 return bRetval;
3212 // #i37011# centralize throw-away of render geometry
3213 void SdrObjCustomShape::InvalidateRenderGeometry()
3215 mXRenderedCustomShape = nullptr;
3216 SdrObject::Free( mpLastShadowGeometry );
3217 mpLastShadowGeometry = nullptr;
3220 void SdrObjCustomShape::impl_setUnoShape(const uno::Reference<uno::XInterface>& rxUnoShape)
3222 SdrTextObj::impl_setUnoShape(rxUnoShape);
3224 // The shape engine is created with _current_ shape. This means we
3225 // _must_ reset it when the shape changes.
3226 mxCustomShapeEngine.set(nullptr);
3229 OUString SdrObjCustomShape::GetCustomShapeName() const
3231 OUString sShapeName;
3232 OUString aEngine( GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
3233 if ( aEngine.isEmpty()
3234 || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
3236 OUString sShapeType;
3237 const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
3238 const Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
3239 if ( pAny && ( *pAny >>= sShapeType ) )
3240 sShapeName = EnhancedCustomShapeTypeNames::GetAccName( sShapeType );
3242 return sShapeName;
3245 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */