Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / svdraw / svdoashp.cxx
blobc86c9ad0e194fb0544855b8aba960eda025120a1
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 <vcl/BitmapShadowFilter.hxx>
21 #include <svx/svdoashp.hxx>
22 #include <svx/unoapi.hxx>
23 #include <com/sun/star/loader/CannotActivateFactoryException.hpp>
24 #include <com/sun/star/drawing/XShape.hpp>
25 #include <com/sun/star/drawing/XCustomShapeEngine.hpp>
26 #include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
27 #include <com/sun/star/beans/PropertyValue.hpp>
28 #include <com/sun/star/awt/Rectangle.hpp>
29 #include <com/sun/star/uno/XComponentContext.hpp>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/propertyvalue.hxx>
32 #include <comphelper/sequenceashashmap.hxx>
33 #include <com/sun/star/uno/Sequence.h>
34 #include <tools/helpers.hxx>
35 #include <svx/svddrag.hxx>
36 #include <svx/svddrgmt.hxx>
37 #include <svx/svdmodel.hxx>
38 #include <svx/svdpage.hxx>
39 #include <svx/svditer.hxx>
40 #include <svx/svdobj.hxx>
41 #include <svx/svdtrans.hxx>
42 #include <svx/dialmgr.hxx>
43 #include <svx/strings.hrc>
44 #include <editeng/eeitem.hxx>
45 #include <editeng/editstat.hxx>
46 #include <editeng/adjustitem.hxx>
47 #include <svx/svdoutl.hxx>
48 #include <editeng/outlobj.hxx>
49 #include <svx/sdtfchim.hxx>
50 #include <svx/EnhancedCustomShapeGeometry.hxx>
51 #include <svx/EnhancedCustomShapeTypeNames.hxx>
52 #include <svx/EnhancedCustomShape2d.hxx>
53 #include <com/sun/star/beans/PropertyValues.hpp>
54 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
55 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
56 #include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
57 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
58 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
59 #include <editeng/writingmodeitem.hxx>
60 #include <svx/xlineit0.hxx>
61 #include <svx/xlnclit.hxx>
62 #include <sdr/properties/customshapeproperties.hxx>
63 #include <sdr/contact/viewcontactofsdrobjcustomshape.hxx>
64 #include <svx/xlntrit.hxx>
65 #include <svx/xfillit0.hxx>
66 #include <svx/xfltrit.hxx>
67 #include <svx/xflclit.hxx>
68 #include <svx/xflgrit.hxx>
69 #include <svx/xflhtit.hxx>
70 #include <svx/xbtmpit.hxx>
71 #include <vcl/virdev.hxx>
72 #include <svx/svdview.hxx>
73 #include <svx/sdmetitm.hxx>
74 #include <svx/sdprcitm.hxx>
75 #include <svx/sdshitm.hxx>
76 #include <svx/sdsxyitm.hxx>
77 #include <svx/sdtmfitm.hxx>
78 #include <svx/sdasitm.hxx>
79 #include <basegfx/polygon/b2dpolypolygontools.hxx>
80 #include <basegfx/matrix/b2dhommatrix.hxx>
81 #include <basegfx/matrix/b2dhommatrixtools.hxx>
82 #include <basegfx/polygon/b2dpolygon.hxx>
83 #include <basegfx/polygon/b2dpolygontools.hxx>
84 #include <basegfx/range/b2drange.hxx>
85 #include <svdobjplusdata.hxx>
86 #include <sal/log.hxx>
87 #include <o3tl/string_view.hxx>
88 #include "presetooxhandleadjustmentrelations.hxx"
89 #include <editeng/frmdiritem.hxx>
91 using namespace ::com::sun::star;
93 static void lcl_ShapeSegmentFromBinary( drawing::EnhancedCustomShapeSegment& rSegInfo, sal_uInt16 nSDat )
95 switch( nSDat >> 8 )
97 case 0x00 :
98 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
99 rSegInfo.Count = nSDat & 0xff;
100 if ( !rSegInfo.Count )
101 rSegInfo.Count = 1;
102 break;
103 case 0x20 :
104 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CURVETO;
105 rSegInfo.Count = nSDat & 0xff;
106 if ( !rSegInfo.Count )
107 rSegInfo.Count = 1;
108 break;
109 case 0x40 :
110 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
111 rSegInfo.Count = nSDat & 0xff;
112 if ( !rSegInfo.Count )
113 rSegInfo.Count = 1;
114 break;
115 case 0x60 :
116 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
117 rSegInfo.Count = 0;
118 break;
119 case 0x80 :
120 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
121 rSegInfo.Count = 0;
122 break;
123 case 0xa1 :
124 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO;
125 rSegInfo.Count = ( nSDat & 0xff ) / 3;
126 break;
127 case 0xa2 :
128 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE;
129 rSegInfo.Count = ( nSDat & 0xff ) / 3;
130 break;
131 case 0xa3 :
132 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ARCTO;
133 rSegInfo.Count = ( nSDat & 0xff ) >> 2;
134 break;
135 case 0xa4 :
136 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ARC;
137 rSegInfo.Count = ( nSDat & 0xff ) >> 2;
138 break;
139 case 0xa5 :
140 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO;
141 rSegInfo.Count = ( nSDat & 0xff ) >> 2;
142 break;
143 case 0xa6 :
144 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC;
145 rSegInfo.Count = ( nSDat & 0xff ) >> 2;
146 break;
147 case 0xa7 :
148 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX;
149 rSegInfo.Count = nSDat & 0xff;
150 break;
151 case 0xa8 :
152 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY;
153 rSegInfo.Count = nSDat & 0xff;
154 break;
155 case 0xaa :
156 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::NOFILL;
157 rSegInfo.Count = 0;
158 break;
159 case 0xab :
160 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::NOSTROKE;
161 rSegInfo.Count = 0;
162 break;
163 default:
164 case 0xf8 :
165 rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::UNKNOWN;
166 rSegInfo.Count = nSDat;
167 break;
171 static MSO_SPT ImpGetCustomShapeType( const SdrObjCustomShape& rCustoShape )
173 MSO_SPT eRetValue = mso_sptNil;
175 OUString aEngine( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
176 if ( aEngine.isEmpty() || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
178 OUString sShapeType;
179 const SdrCustomShapeGeometryItem& rGeometryItem( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
180 const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
181 if ( pAny && ( *pAny >>= sShapeType ) )
182 eRetValue = EnhancedCustomShapeTypeNames::Get( sShapeType );
184 return eRetValue;
187 static bool ImpVerticalSwitch( const SdrObjCustomShape& rCustoShape )
189 bool bRet = false;
190 MSO_SPT eShapeType( ImpGetCustomShapeType( rCustoShape ) );
191 switch( eShapeType )
193 case mso_sptAccentBorderCallout90 : // 2 ortho
194 case mso_sptBorderCallout1 : // 2 diag
195 case mso_sptBorderCallout2 : // 3
197 bRet = true;
199 break;
200 default: break;
202 return bRet;
205 // #i37011# create a clone with all attributes changed to shadow attributes
206 // and translation executed, too.
207 static rtl::Reference<SdrObject> ImpCreateShadowObjectClone(const SdrObject& rOriginal, const SfxItemSet& rOriginalSet)
209 rtl::Reference<SdrObject> pRetval;
210 const bool bShadow(rOriginalSet.Get(SDRATTR_SHADOW).GetValue());
212 if(bShadow)
214 // create a shadow representing object
215 const sal_Int32 nXDist(rOriginalSet.Get(SDRATTR_SHADOWXDIST).GetValue());
216 const sal_Int32 nYDist(rOriginalSet.Get(SDRATTR_SHADOWYDIST).GetValue());
217 const ::Color aShadowColor(rOriginalSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue());
218 const sal_uInt16 nShadowTransparence(rOriginalSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue());
219 pRetval = rOriginal.CloneSdrObject(rOriginal.getSdrModelFromSdrObject());
220 DBG_ASSERT(pRetval, "ImpCreateShadowObjectClone: Could not clone object (!)");
222 // look for used stuff
223 SdrObjListIter aIterator(rOriginal);
224 bool bLineUsed(false);
225 bool bAllFillUsed(false);
226 bool bSolidFillUsed(false);
227 bool bGradientFillUsed(false);
228 bool bHatchFillUsed(false);
229 bool bBitmapFillUsed(false);
231 while(aIterator.IsMore())
233 SdrObject* pObj = aIterator.Next();
234 drawing::FillStyle eFillStyle = pObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
236 if(!bLineUsed)
238 drawing::LineStyle eLineStyle = pObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
240 if(drawing::LineStyle_NONE != eLineStyle)
242 bLineUsed = true;
246 if(!bAllFillUsed)
248 if(!bSolidFillUsed && drawing::FillStyle_SOLID == eFillStyle)
250 bSolidFillUsed = true;
251 bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
253 if(!bGradientFillUsed && drawing::FillStyle_GRADIENT == eFillStyle)
255 bGradientFillUsed = true;
256 bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
258 if(!bHatchFillUsed && drawing::FillStyle_HATCH == eFillStyle)
260 bHatchFillUsed = true;
261 bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
263 if(!bBitmapFillUsed && drawing::FillStyle_BITMAP == eFillStyle)
265 bBitmapFillUsed = true;
266 bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
271 // translate to shadow coordinates
272 pRetval->NbcMove(Size(nXDist, nYDist));
274 // set items as needed
275 SfxItemSet aTempSet(rOriginalSet);
277 // if a SvxWritingModeItem (Top->Bottom) is set the text object
278 // is creating a paraobject, but paraobjects can not be created without model. So
279 // we are preventing the crash by setting the writing mode always left to right,
280 // this is not bad since our shadow geometry does not contain text.
281 aTempSet.Put(SvxWritingModeItem(text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION));
283 // no shadow
284 aTempSet.Put(makeSdrShadowItem(false));
285 aTempSet.Put(makeSdrShadowXDistItem(0));
286 aTempSet.Put(makeSdrShadowYDistItem(0));
288 // line color and transparency like shadow
289 if(bLineUsed)
291 aTempSet.Put(XLineColorItem(OUString(), aShadowColor));
292 aTempSet.Put(XLineTransparenceItem(nShadowTransparence));
295 // fill color and transparency like shadow
296 if(bSolidFillUsed)
298 aTempSet.Put(XFillColorItem(OUString(), aShadowColor));
299 aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
302 // gradient and transparency like shadow
303 if(bGradientFillUsed)
305 basegfx::BGradient aGradient(rOriginalSet.Get(XATTR_FILLGRADIENT).GetGradientValue());
306 sal_uInt8 nStartLuminance(Color(aGradient.GetColorStops().front().getStopColor()).GetLuminance());
307 sal_uInt8 nEndLuminance(Color(aGradient.GetColorStops().back().getStopColor()).GetLuminance());
309 if(aGradient.GetStartIntens() != 100)
311 nStartLuminance = static_cast<sal_uInt8>(nStartLuminance * (static_cast<double>(aGradient.GetStartIntens()) / 100.0));
314 if(aGradient.GetEndIntens() != 100)
316 nEndLuminance = static_cast<sal_uInt8>(nEndLuminance * (static_cast<double>(aGradient.GetEndIntens()) / 100.0));
319 ::Color aStartColor(
320 static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetRed()) / 256),
321 static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetGreen()) / 256),
322 static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetBlue()) / 256));
324 ::Color aEndColor(
325 static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetRed()) / 256),
326 static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetGreen()) / 256),
327 static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetBlue()) / 256));
329 aGradient.SetColorStops(
330 basegfx::BColorStops(
331 aStartColor.getBColor(),
332 aEndColor.getBColor()));
333 aTempSet.Put(XFillGradientItem(aGradient));
334 aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
337 // hatch and transparency like shadow
338 if(bHatchFillUsed)
340 XHatch aHatch(rOriginalSet.Get(XATTR_FILLHATCH).GetHatchValue());
341 aHatch.SetColor(aShadowColor);
342 aTempSet.Put(XFillHatchItem(aHatch));
343 aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
346 // bitmap and transparency like shadow
347 if(bBitmapFillUsed)
349 GraphicObject aGraphicObject(rOriginalSet.Get(XATTR_FILLBITMAP).GetGraphicObject());
350 BitmapEx aBitmapEx(aGraphicObject.GetGraphic().GetBitmapEx());
352 if(!aBitmapEx.IsEmpty())
354 ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
355 pVirDev->SetOutputSizePixel(aBitmapEx.GetSizePixel());
356 BitmapFilter::Filter(aBitmapEx, BitmapShadowFilter(aShadowColor));
357 pVirDev->DrawBitmapEx(Point(), aBitmapEx);
358 aGraphicObject.SetGraphic(Graphic(pVirDev->GetBitmapEx(Point(0,0), aBitmapEx.GetSizePixel())));
361 aTempSet.Put(XFillBitmapItem(aGraphicObject));
362 aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
365 // set attributes and paint shadow object
366 pRetval->SetMergedItemSet( aTempSet );
368 return pRetval;
372 uno::Reference<drawing::XCustomShapeEngine> const & SdrObjCustomShape::GetCustomShapeEngine() const
374 if (mxCustomShapeEngine.is())
375 return mxCustomShapeEngine;
377 uno::Reference<drawing::XShape> aXShape = GetXShapeForSdrObject(const_cast<SdrObjCustomShape*>(this));
378 if ( !aXShape )
379 return mxCustomShapeEngine;
381 uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
383 OUString aEngine(GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue());
384 static constexpr OUStringLiteral sEnhancedCustomShapeEngine = u"com.sun.star.drawing.EnhancedCustomShapeEngine";
385 if ( aEngine.isEmpty() )
386 aEngine = sEnhancedCustomShapeEngine;
389 static constexpr OUStringLiteral sCustomShape = u"CustomShape";
390 uno::Sequence<beans::PropertyValue> aPropValues{ comphelper::makePropertyValue(sCustomShape,
391 aXShape) };
392 uno::Sequence<uno::Any> aArgument{ uno::Any(aPropValues) };
395 uno::Reference<uno::XInterface> xInterface(xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aEngine, aArgument, xContext));
396 if (xInterface.is())
397 mxCustomShapeEngine.set(xInterface, uno::UNO_QUERY);
399 catch (const loader::CannotActivateFactoryException&)
404 return mxCustomShapeEngine;
407 const SdrObject* SdrObjCustomShape::GetSdrObjectFromCustomShape() const
409 if ( !mXRenderedCustomShape.is() )
411 uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
412 if ( xCustomShapeEngine.is() )
413 const_cast<SdrObjCustomShape*>(this)->mXRenderedCustomShape = xCustomShapeEngine->render();
415 SdrObject* pRenderedCustomShape = mXRenderedCustomShape.is()
416 ? SdrObject::getSdrObjectFromXShape( mXRenderedCustomShape )
417 : nullptr;
418 return pRenderedCustomShape;
421 // #i37011# Shadow geometry creation
422 const SdrObject* SdrObjCustomShape::GetSdrObjectShadowFromCustomShape() const
424 if(!mpLastShadowGeometry)
426 const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
427 if(pSdrObject)
429 const SfxItemSet& rOriginalSet = GetObjectItemSet();
430 const bool bShadow(rOriginalSet.Get( SDRATTR_SHADOW ).GetValue());
432 if(bShadow)
434 // create a clone with all attributes changed to shadow attributes
435 // and translation executed, too.
436 const_cast<SdrObjCustomShape*>(this)->mpLastShadowGeometry =
437 ImpCreateShadowObjectClone(*pSdrObject, rOriginalSet);
442 return mpLastShadowGeometry.get();
445 bool SdrObjCustomShape::IsTextPath() const
447 static const OUStringLiteral sTextPath( u"TextPath" );
448 bool bTextPathOn = false;
449 const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
450 const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sTextPath, sTextPath );
451 if ( pAny )
452 *pAny >>= bTextPathOn;
453 return bTextPathOn;
456 bool SdrObjCustomShape::UseNoFillStyle() const
458 bool bRet = false;
459 OUString sShapeType;
460 static const OUStringLiteral sType( u"Type" );
461 const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
462 const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sType );
463 if ( pAny )
464 *pAny >>= sShapeType;
465 bRet = !IsCustomShapeFilledByDefault( EnhancedCustomShapeTypeNames::Get( sType ) );
467 return bRet;
470 bool SdrObjCustomShape::IsMirroredX() const
472 bool bMirroredX = false;
473 const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
474 const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "MirroredX" );
475 if ( pAny )
476 *pAny >>= bMirroredX;
477 return bMirroredX;
479 bool SdrObjCustomShape::IsMirroredY() const
481 bool bMirroredY = false;
482 const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
483 const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "MirroredY" );
484 if ( pAny )
485 *pAny >>= bMirroredY;
486 return bMirroredY;
488 void SdrObjCustomShape::SetMirroredX( const bool bMirrorX )
490 SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
491 beans::PropertyValue aPropVal;
492 aPropVal.Name = "MirroredX";
493 aPropVal.Value <<= bMirrorX;
494 aGeometryItem.SetPropertyValue( aPropVal );
495 SetMergedItem( aGeometryItem );
497 void SdrObjCustomShape::SetMirroredY( const bool bMirrorY )
499 SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
500 beans::PropertyValue aPropVal;
501 aPropVal.Name = "MirroredY";
502 aPropVal.Value <<= bMirrorY;
503 aGeometryItem.SetPropertyValue( aPropVal );
504 SetMergedItem( aGeometryItem );
507 double SdrObjCustomShape::GetExtraTextRotation( const bool bPreRotation ) const
509 double fExtraTextRotateAngle = 0.0;
510 if (bPreRotation)
512 // textPreRotateAngle might be set by macro or diagram (SmartArt) import
513 const uno::Any* pAny;
514 const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
515 pAny = rGeometryItem.GetPropertyValueByName(u"TextPreRotateAngle");
516 if ( pAny )
517 *pAny >>= fExtraTextRotateAngle;
519 // As long as the edit engine is not able to render these text directions we
520 // emulate them by setting a suitable text pre-rotation.
521 const SvxFrameDirectionItem& rDirectionItem = GetMergedItem(SDRATTR_WRITINGMODE2);
522 if (rDirectionItem.GetValue() == SvxFrameDirection::Vertical_RL_TB90)
523 fExtraTextRotateAngle -= 90;
524 else if (rDirectionItem.GetValue() == SvxFrameDirection::Vertical_LR_BT)
525 fExtraTextRotateAngle -=270;
527 else
529 const uno::Any* pAny;
530 const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
531 pAny = rGeometryItem.GetPropertyValueByName(u"TextRotateAngle");
532 if ( pAny )
533 *pAny >>= fExtraTextRotateAngle;
535 return fExtraTextRotateAngle;
538 bool SdrObjCustomShape::GetTextBounds( tools::Rectangle& rTextBound ) const
540 bool bRet = false;
542 uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
543 if ( xCustomShapeEngine.is() )
545 awt::Rectangle aR( xCustomShapeEngine->getTextBounds() );
546 if ( aR.Width > 1 && aR.Height > 1 )
548 rTextBound = tools::Rectangle( Point( aR.X, aR.Y ), Size( aR.Width, aR.Height ) );
549 bRet = true;
552 return bRet;
554 basegfx::B2DPolyPolygon SdrObjCustomShape::GetLineGeometry( const bool bBezierAllowed ) const
556 basegfx::B2DPolyPolygon aRetval;
557 uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
558 if ( xCustomShapeEngine.is() )
560 drawing::PolyPolygonBezierCoords aBezierCoords = xCustomShapeEngine->getLineGeometry();
563 aRetval = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon( aBezierCoords );
564 if ( !bBezierAllowed && aRetval.areControlPointsUsed())
566 aRetval = basegfx::utils::adaptiveSubdivideByAngle(aRetval);
569 catch ( const lang::IllegalArgumentException & )
573 return aRetval;
576 std::vector< SdrCustomShapeInteraction > SdrObjCustomShape::GetInteractionHandles() const
578 std::vector< SdrCustomShapeInteraction > aRet;
581 uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
582 if ( xCustomShapeEngine.is() )
584 int i;
585 uno::Sequence<uno::Reference<drawing::XCustomShapeHandle>> xInteractionHandles( xCustomShapeEngine->getInteraction() );
586 for ( i = 0; i < xInteractionHandles.getLength(); i++ )
588 if ( xInteractionHandles[ i ].is() )
590 SdrCustomShapeInteraction aSdrCustomShapeInteraction;
591 aSdrCustomShapeInteraction.xInteraction = xInteractionHandles[ i ];
592 aSdrCustomShapeInteraction.aPosition = xInteractionHandles[ i ]->getPosition();
594 CustomShapeHandleModes nMode = CustomShapeHandleModes::NONE;
595 switch( ImpGetCustomShapeType( *this ) )
597 case mso_sptAccentBorderCallout90 : // 2 ortho
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 | CustomShapeHandleModes::ORTHO4;
604 break;
606 case mso_sptChevron :
607 case mso_sptHomePlate :
608 nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX;
609 break;
611 case mso_sptWedgeRectCallout :
612 case mso_sptWedgeRRectCallout :
613 case mso_sptCloudCallout :
614 case mso_sptWedgeEllipseCallout :
616 if (i == 0)
617 nMode |= CustomShapeHandleModes::RESIZE_FIXED;
619 break;
621 case mso_sptBorderCallout1 : // 2 diag
623 if (i == 0)
624 nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
625 else if (i == 1)
626 nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
628 break;
629 case mso_sptBorderCallout2 : // 3
631 if (i == 0)
632 nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
633 else if (i == 2)
634 nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
636 break;
637 case mso_sptCallout90 :
638 case mso_sptAccentCallout90 :
639 case mso_sptBorderCallout90 :
640 case mso_sptCallout1 :
641 case mso_sptCallout2 :
642 case mso_sptCallout3 :
643 case mso_sptAccentCallout1 :
644 case mso_sptAccentCallout2 :
645 case mso_sptAccentCallout3 :
646 case mso_sptBorderCallout3 :
647 case mso_sptAccentBorderCallout1 :
648 case mso_sptAccentBorderCallout2 :
649 case mso_sptAccentBorderCallout3 :
651 if (i == 0)
652 nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
654 break;
655 default: break;
657 aSdrCustomShapeInteraction.nMode = nMode;
658 aRet.push_back( aSdrCustomShapeInteraction );
663 catch( const uno::RuntimeException& )
666 return aRet;
670 // BaseProperties section
671 #define DEFAULT_MINIMUM_SIGNED_COMPARE (sal_Int32(0x80000000))
672 #define DEFAULT_MAXIMUM_SIGNED_COMPARE (sal_Int32(0x7fffffff))
674 static sal_Int32 GetNumberOfProperties ( const SvxMSDffHandle* pData )
676 sal_Int32 nPropertiesNeeded=1; // position is always needed
677 SvxMSDffHandleFlags nFlags = pData->nFlags;
679 if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
680 nPropertiesNeeded++;
681 if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
682 nPropertiesNeeded++;
683 if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
684 nPropertiesNeeded++;
685 if ( nFlags & SvxMSDffHandleFlags::POLAR )
687 nPropertiesNeeded++;
688 if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
690 if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
691 nPropertiesNeeded++;
692 if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
693 nPropertiesNeeded++;
696 else if ( nFlags & SvxMSDffHandleFlags::RANGE )
698 if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
699 nPropertiesNeeded++;
700 if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
701 nPropertiesNeeded++;
702 if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
703 nPropertiesNeeded++;
704 if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
705 nPropertiesNeeded++;
708 return nPropertiesNeeded;
711 static void lcl_ShapePropertiesFromDFF( const SvxMSDffHandle* pData, beans::PropertyValues& rPropValues )
713 SvxMSDffHandleFlags nFlags = pData->nFlags;
714 sal_Int32 n=0;
715 auto pPropValues = rPropValues.getArray();
717 // POSITION
719 drawing::EnhancedCustomShapeParameterPair aPosition;
720 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.First, pData->nPositionX, true, true );
721 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.Second, pData->nPositionY, true, false );
722 pPropValues[ n ].Name = "Position";
723 pPropValues[ n++ ].Value <<= aPosition;
725 if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
727 pPropValues[ n ].Name = "MirroredX";
728 pPropValues[ n++ ].Value <<= true;
730 if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
732 pPropValues[ n ].Name = "MirroredY";
733 pPropValues[ n++ ].Value <<= true;
735 if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
737 pPropValues[ n ].Name = "Switched";
738 pPropValues[ n++ ].Value <<= true;
740 if ( nFlags & SvxMSDffHandleFlags::POLAR )
742 drawing::EnhancedCustomShapeParameterPair aCenter;
743 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.First, pData->nCenterX,
744 bool( nFlags & SvxMSDffHandleFlags::CENTER_X_IS_SPECIAL ), true );
745 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.Second, pData->nCenterY,
746 bool( nFlags & SvxMSDffHandleFlags::CENTER_Y_IS_SPECIAL ), false );
747 pPropValues[ n ].Name = "Polar";
748 pPropValues[ n++ ].Value <<= aCenter;
749 if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
751 if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
753 drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum;
754 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMinimum, pData->nRangeXMin,
755 bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
756 pPropValues[ n ].Name = "RadiusRangeMinimum";
757 pPropValues[ n++ ].Value <<= aRadiusRangeMinimum;
759 if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
761 drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum;
762 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMaximum, pData->nRangeXMax,
763 bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
764 pPropValues[ n ].Name = "RadiusRangeMaximum";
765 pPropValues[ n++ ].Value <<= aRadiusRangeMaximum;
769 else if ( nFlags & SvxMSDffHandleFlags::RANGE )
771 if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
773 drawing::EnhancedCustomShapeParameter aRangeXMinimum;
774 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMinimum, pData->nRangeXMin,
775 bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
776 pPropValues[ n ].Name = "RangeXMinimum";
777 pPropValues[ n++ ].Value <<= aRangeXMinimum;
779 if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
781 drawing::EnhancedCustomShapeParameter aRangeXMaximum;
782 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMaximum, pData->nRangeXMax,
783 bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
784 pPropValues[ n ].Name = "RangeXMaximum";
785 pPropValues[ n++ ].Value <<= aRangeXMaximum;
787 if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
789 drawing::EnhancedCustomShapeParameter aRangeYMinimum;
790 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMinimum, pData->nRangeYMin,
791 bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL ), true );
792 pPropValues[ n ].Name = "RangeYMinimum";
793 pPropValues[ n++ ].Value <<= aRangeYMinimum;
795 if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
797 drawing::EnhancedCustomShapeParameter aRangeYMaximum;
798 EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMaximum, pData->nRangeYMax,
799 bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL ), false );
800 pPropValues[ n ].Name = "RangeYMaximum";
801 pPropValues[ n++ ].Value <<= aRangeYMaximum;
806 std::unique_ptr<sdr::properties::BaseProperties> SdrObjCustomShape::CreateObjectSpecificProperties()
808 return std::make_unique<sdr::properties::CustomShapeProperties>(*this);
811 SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel)
812 : SdrTextObj(rSdrModel)
813 , fObjectRotation(0.0)
814 , mbAdjustingTextFrameWidthAndHeight(false)
816 m_bClosedObj = true; // custom shapes may be filled
817 mbTextFrame = true;
820 SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel, SdrObjCustomShape const & rSource)
821 : SdrTextObj(rSdrModel, rSource)
822 , fObjectRotation(0.0)
823 , mbAdjustingTextFrameWidthAndHeight(false)
825 m_bClosedObj = true; // custom shapes may be filled
826 mbTextFrame = true;
828 fObjectRotation = rSource.fObjectRotation;
829 mbAdjustingTextFrameWidthAndHeight = rSource.mbAdjustingTextFrameWidthAndHeight;
830 assert(!mbAdjustingTextFrameWidthAndHeight);
831 InvalidateRenderGeometry();
834 SdrObjCustomShape::~SdrObjCustomShape()
836 // delete buffered display geometry
837 InvalidateRenderGeometry();
840 void SdrObjCustomShape::MergeDefaultAttributes( const OUString* pType )
842 beans::PropertyValue aPropVal;
843 OUString sShapeType;
844 static const OUStringLiteral sType( u"Type" );
845 SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
846 if ( pType && !pType->isEmpty() )
848 sal_Int32 nType = pType->toInt32();
849 if ( nType )
850 sShapeType = EnhancedCustomShapeTypeNames::Get( static_cast< MSO_SPT >( nType ) );
851 else
852 sShapeType = *pType;
854 aPropVal.Name = sType;
855 aPropVal.Value <<= sShapeType;
856 aGeometryItem.SetPropertyValue( aPropVal );
858 else
860 uno::Any *pAny = aGeometryItem.GetPropertyValueByName( sType );
861 if ( pAny )
862 *pAny >>= sShapeType;
864 MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
866 const sal_Int32* pDefData = nullptr;
867 const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
868 if ( pDefCustomShape )
869 pDefData = pDefCustomShape->pDefData;
871 uno::Sequence<drawing::EnhancedCustomShapeAdjustmentValue> seqAdjustmentValues;
874 // AdjustmentValues
876 static const OUStringLiteral sAdjustmentValues( u"AdjustmentValues" );
877 const uno::Any* pAny = aGeometryItem.GetPropertyValueByName( sAdjustmentValues );
878 if ( pAny )
879 *pAny >>= seqAdjustmentValues;
880 if ( pDefCustomShape && pDefData ) // now check if we have to default some adjustment values
882 // first check if there are adjustment values are to be appended
883 sal_Int32 i, nAdjustmentValues = seqAdjustmentValues.getLength();
884 sal_Int32 nAdjustmentDefaults = *pDefData++;
885 if ( nAdjustmentDefaults > nAdjustmentValues )
886 seqAdjustmentValues.realloc( nAdjustmentDefaults );
887 auto pseqAdjustmentValues = seqAdjustmentValues.getArray();
888 for ( i = nAdjustmentValues; i < nAdjustmentDefaults; i++ )
890 pseqAdjustmentValues[ i ].Value <<= pDefData[ i ];
891 pseqAdjustmentValues[ i ].State = beans::PropertyState_DIRECT_VALUE;
893 // check if there are defaulted adjustment values that should be filled the hard coded defaults (pDefValue)
894 sal_Int32 nCount = std::min(nAdjustmentValues, nAdjustmentDefaults);
895 for ( i = 0; i < nCount; i++ )
897 if ( seqAdjustmentValues[ i ].State != beans::PropertyState_DIRECT_VALUE )
899 pseqAdjustmentValues[ i ].Value <<= pDefData[ i ];
900 pseqAdjustmentValues[ i ].State = beans::PropertyState_DIRECT_VALUE;
904 aPropVal.Name = sAdjustmentValues;
905 aPropVal.Value <<= seqAdjustmentValues;
906 aGeometryItem.SetPropertyValue( aPropVal );
909 // Coordsize
911 static const OUStringLiteral sViewBox( u"ViewBox" );
912 const uno::Any* pViewBox = aGeometryItem.GetPropertyValueByName( sViewBox );
913 awt::Rectangle aViewBox;
914 if ( !pViewBox || !(*pViewBox >>= aViewBox ) )
916 if ( pDefCustomShape )
918 aViewBox.X = 0;
919 aViewBox.Y = 0;
920 aViewBox.Width = pDefCustomShape->nCoordWidth;
921 aViewBox.Height= pDefCustomShape->nCoordHeight;
922 aPropVal.Name = sViewBox;
923 aPropVal.Value <<= aViewBox;
924 aGeometryItem.SetPropertyValue( aPropVal );
928 static const OUStringLiteral sPath( u"Path" );
931 // Path/Coordinates
933 static const OUStringLiteral sCoordinates( u"Coordinates" );
934 pAny = aGeometryItem.GetPropertyValueByName( sPath, sCoordinates );
935 if ( !pAny && pDefCustomShape && pDefCustomShape->nVertices && pDefCustomShape->pVertices )
937 sal_Int32 i, nCount = pDefCustomShape->nVertices;
938 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqCoordinates( nCount );
939 auto pseqCoordinates = seqCoordinates.getArray();
940 for ( i = 0; i < nCount; i++ )
942 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
943 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
945 aPropVal.Name = sCoordinates;
946 aPropVal.Value <<= seqCoordinates;
947 aGeometryItem.SetPropertyValue( sPath, aPropVal );
950 // Path/GluePoints
951 static const OUStringLiteral sGluePoints( u"GluePoints" );
952 pAny = aGeometryItem.GetPropertyValueByName( sPath, sGluePoints );
953 if ( !pAny && pDefCustomShape && pDefCustomShape->nGluePoints && pDefCustomShape->pGluePoints )
955 sal_Int32 i, nCount = pDefCustomShape->nGluePoints;
956 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqGluePoints( nCount );
957 auto pseqGluePoints = seqGluePoints.getArray();
958 for ( i = 0; i < nCount; i++ )
960 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
961 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
963 aPropVal.Name = sGluePoints;
964 aPropVal.Value <<= seqGluePoints;
965 aGeometryItem.SetPropertyValue( sPath, aPropVal );
968 // Path/Segments
969 static const OUStringLiteral sSegments( u"Segments" );
970 pAny = aGeometryItem.GetPropertyValueByName( sPath, sSegments );
971 if ( !pAny && pDefCustomShape && pDefCustomShape->nElements && pDefCustomShape->pElements )
973 sal_Int32 i, nCount = pDefCustomShape->nElements;
974 uno::Sequence<drawing::EnhancedCustomShapeSegment> seqSegments( nCount );
975 auto pseqSegments = seqSegments.getArray();
976 for ( i = 0; i < nCount; i++ )
978 drawing::EnhancedCustomShapeSegment& rSegInfo = pseqSegments[ i ];
979 sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
980 lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
982 aPropVal.Name = sSegments;
983 aPropVal.Value <<= seqSegments;
984 aGeometryItem.SetPropertyValue( sPath, aPropVal );
987 // Path/StretchX
988 static const OUStringLiteral sStretchX( u"StretchX" );
989 pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchX );
990 if ( !pAny && pDefCustomShape )
992 sal_Int32 nXRef = pDefCustomShape->nXRef;
993 if ( nXRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
995 aPropVal.Name = sStretchX;
996 aPropVal.Value <<= nXRef;
997 aGeometryItem.SetPropertyValue( sPath, aPropVal );
1001 // Path/StretchY
1002 static const OUStringLiteral sStretchY( u"StretchY" );
1003 pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchY );
1004 if ( !pAny && pDefCustomShape )
1006 sal_Int32 nYRef = pDefCustomShape->nYRef;
1007 if ( nYRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
1009 aPropVal.Name = sStretchY;
1010 aPropVal.Value <<= nYRef;
1011 aGeometryItem.SetPropertyValue( sPath, aPropVal );
1015 // Path/TextFrames
1016 static const OUStringLiteral sTextFrames( u"TextFrames" );
1017 pAny = aGeometryItem.GetPropertyValueByName( sPath, sTextFrames );
1018 if ( !pAny && pDefCustomShape && pDefCustomShape->nTextRect && pDefCustomShape->pTextRect )
1020 sal_Int32 i, nCount = pDefCustomShape->nTextRect;
1021 uno::Sequence<drawing::EnhancedCustomShapeTextFrame> seqTextFrames( nCount );
1022 auto pseqTextFrames = seqTextFrames.getArray();
1023 const SvxMSDffTextRectangles* pRectangles = pDefCustomShape->pTextRect;
1024 for ( i = 0; i < nCount; i++, pRectangles++ )
1026 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].TopLeft.First, pRectangles->nPairA.nValA );
1027 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
1028 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].BottomRight.First, pRectangles->nPairB.nValA );
1029 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
1031 aPropVal.Name = sTextFrames;
1032 aPropVal.Value <<= seqTextFrames;
1033 aGeometryItem.SetPropertyValue( sPath, aPropVal );
1036 // Equations
1037 static const OUStringLiteral sEquations( u"Equations" );
1038 pAny = aGeometryItem.GetPropertyValueByName( sEquations );
1039 if ( !pAny && pDefCustomShape && pDefCustomShape->nCalculation && pDefCustomShape->pCalculation )
1041 sal_Int32 i, nCount = pDefCustomShape->nCalculation;
1042 uno::Sequence< OUString > seqEquations( nCount );
1043 auto pseqEquations = seqEquations.getArray();
1044 const SvxMSDffCalculationData* pData = pDefCustomShape->pCalculation;
1045 for ( i = 0; i < nCount; i++, pData++ )
1046 pseqEquations[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
1047 aPropVal.Name = sEquations;
1048 aPropVal.Value <<= seqEquations;
1049 aGeometryItem.SetPropertyValue( aPropVal );
1052 // Handles
1053 static const OUStringLiteral sHandles( u"Handles" );
1054 pAny = aGeometryItem.GetPropertyValueByName( sHandles );
1055 if ( !pAny && pDefCustomShape && pDefCustomShape->nHandles && pDefCustomShape->pHandles )
1057 sal_Int32 i, nCount = pDefCustomShape->nHandles;
1058 const SvxMSDffHandle* pData = pDefCustomShape->pHandles;
1059 uno::Sequence<beans::PropertyValues> seqHandles( nCount );
1060 auto pseqHandles = seqHandles.getArray();
1061 for ( i = 0; i < nCount; i++, pData++ )
1063 sal_Int32 nPropertiesNeeded;
1064 beans::PropertyValues& rPropValues = pseqHandles[ i ];
1065 nPropertiesNeeded = GetNumberOfProperties( pData );
1066 rPropValues.realloc( nPropertiesNeeded );
1067 lcl_ShapePropertiesFromDFF( pData, rPropValues );
1069 aPropVal.Name = sHandles;
1070 aPropVal.Value <<= seqHandles;
1071 aGeometryItem.SetPropertyValue( aPropVal );
1073 else if (pAny && sShapeType.startsWith("ooxml-") && sShapeType != "ooxml-non-primitive")
1075 // ODF is not able to store the ooxml way of connecting handle to an adjustment
1076 // value by name, e.g. attribute RefX="adj". So the information is lost, when exporting
1077 // a pptx to odp, for example. This part reconstructs this information for the
1078 // ooxml preset shapes from their definition.
1079 uno::Sequence<beans::PropertyValues> seqHandles;
1080 *pAny >>= seqHandles;
1081 auto seqHandlesRange = asNonConstRange(seqHandles);
1082 bool bChanged(false);
1083 for (sal_Int32 i = 0; i < seqHandles.getLength(); i++)
1085 comphelper::SequenceAsHashMap aHandleProps(seqHandles[i]);
1086 OUString sFirstRefType;
1087 sal_Int32 nFirstAdjRef;
1088 OUString sSecondRefType;
1089 sal_Int32 nSecondAdjRef;
1090 PresetOOXHandleAdj::GetOOXHandleAdjRelation(sShapeType, i, sFirstRefType, nFirstAdjRef,
1091 sSecondRefType, nSecondAdjRef);
1092 if (sFirstRefType != "na" && 0 <= nFirstAdjRef
1093 && nFirstAdjRef < seqAdjustmentValues.getLength())
1095 bChanged |= aHandleProps.createItemIfMissing(sFirstRefType, nFirstAdjRef);
1097 if (sSecondRefType != "na" && 0 <= nSecondAdjRef
1098 && nSecondAdjRef < seqAdjustmentValues.getLength())
1100 bChanged |= aHandleProps.createItemIfMissing(sSecondRefType, nSecondAdjRef);
1102 aHandleProps >> seqHandlesRange[i];
1104 if (bChanged)
1106 aPropVal.Name = sHandles;
1107 aPropVal.Value <<= seqHandles;
1108 aGeometryItem.SetPropertyValue(aPropVal);
1112 SetMergedItem( aGeometryItem );
1115 bool SdrObjCustomShape::IsDefaultGeometry( const DefaultType eDefaultType ) const
1117 bool bIsDefaultGeometry = false;
1119 OUString sShapeType;
1120 const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
1122 const uno::Any *pAny = rGeometryItem.GetPropertyValueByName( "Type" );
1123 if ( pAny )
1124 *pAny >>= sShapeType;
1126 MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
1128 const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
1129 static const OUStringLiteral sPath( u"Path" );
1130 switch( eDefaultType )
1132 case DefaultType::Viewbox :
1134 const uno::Any* pViewBox = rGeometryItem.GetPropertyValueByName( "ViewBox" );
1135 awt::Rectangle aViewBox;
1136 if (pViewBox && (*pViewBox >>= aViewBox) && pDefCustomShape)
1138 if ( ( aViewBox.Width == pDefCustomShape->nCoordWidth )
1139 && ( aViewBox.Height == pDefCustomShape->nCoordHeight ) )
1140 bIsDefaultGeometry = true;
1143 break;
1145 case DefaultType::Path :
1147 pAny = rGeometryItem.GetPropertyValueByName( sPath, "Coordinates" );
1148 if ( pAny && pDefCustomShape && pDefCustomShape->nVertices && pDefCustomShape->pVertices )
1150 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqCoordinates1;
1151 if ( *pAny >>= seqCoordinates1 )
1153 sal_Int32 i, nCount = pDefCustomShape->nVertices;
1154 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqCoordinates2( nCount );
1155 auto pseqCoordinates2 = seqCoordinates2.getArray();
1156 for ( i = 0; i < nCount; i++ )
1158 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates2[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
1159 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates2[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
1161 if ( seqCoordinates1 == seqCoordinates2 )
1162 bIsDefaultGeometry = true;
1165 else if ( pDefCustomShape && ( ( pDefCustomShape->nVertices == 0 ) || ( pDefCustomShape->pVertices == nullptr ) ) )
1166 bIsDefaultGeometry = true;
1168 break;
1170 case DefaultType::Gluepoints :
1172 pAny = rGeometryItem.GetPropertyValueByName( sPath, "GluePoints" );
1173 if ( pAny && pDefCustomShape && pDefCustomShape->nGluePoints && pDefCustomShape->pGluePoints )
1175 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqGluePoints1;
1176 if ( *pAny >>= seqGluePoints1 )
1178 sal_Int32 i, nCount = pDefCustomShape->nGluePoints;
1179 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqGluePoints2( nCount );
1180 auto pseqGluePoints2 = seqGluePoints2.getArray();
1181 for ( i = 0; i < nCount; i++ )
1183 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints2[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
1184 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints2[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
1186 if ( seqGluePoints1 == seqGluePoints2 )
1187 bIsDefaultGeometry = true;
1190 else if ( pDefCustomShape && ( pDefCustomShape->nGluePoints == 0 ) )
1191 bIsDefaultGeometry = true;
1193 break;
1195 case DefaultType::Segments :
1197 // Path/Segments
1198 pAny = rGeometryItem.GetPropertyValueByName( sPath, "Segments" );
1199 if ( pAny )
1201 uno::Sequence<drawing::EnhancedCustomShapeSegment> seqSegments1;
1202 if ( *pAny >>= seqSegments1 )
1204 if ( pDefCustomShape && pDefCustomShape->nElements && pDefCustomShape->pElements )
1206 sal_Int32 i, nCount = pDefCustomShape->nElements;
1207 if ( nCount )
1209 uno::Sequence<drawing::EnhancedCustomShapeSegment> seqSegments2( nCount );
1210 auto pseqSegments2 = seqSegments2.getArray();
1211 for ( i = 0; i < nCount; i++ )
1213 drawing::EnhancedCustomShapeSegment& rSegInfo = pseqSegments2[ i ];
1214 sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
1215 lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
1217 if ( seqSegments1 == seqSegments2 )
1218 bIsDefaultGeometry = true;
1221 else
1223 // check if it's the default segment description ( M L Z N )
1224 if ( seqSegments1.getLength() == 4 )
1226 if ( ( seqSegments1[ 0 ].Command == drawing::EnhancedCustomShapeSegmentCommand::MOVETO )
1227 && ( seqSegments1[ 1 ].Command == drawing::EnhancedCustomShapeSegmentCommand::LINETO )
1228 && ( seqSegments1[ 2 ].Command == drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
1229 && ( seqSegments1[ 3 ].Command == drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH ) )
1230 bIsDefaultGeometry = true;
1235 else if ( pDefCustomShape && ( ( pDefCustomShape->nElements == 0 ) || ( pDefCustomShape->pElements == nullptr ) ) )
1236 bIsDefaultGeometry = true;
1238 break;
1240 case DefaultType::StretchX :
1242 pAny = rGeometryItem.GetPropertyValueByName( sPath, "StretchX" );
1243 if ( pAny && pDefCustomShape )
1245 sal_Int32 nStretchX = 0;
1246 if ( *pAny >>= nStretchX )
1248 if ( pDefCustomShape->nXRef == nStretchX )
1249 bIsDefaultGeometry = true;
1252 else if ( pDefCustomShape && ( pDefCustomShape->nXRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
1253 bIsDefaultGeometry = true;
1255 break;
1257 case DefaultType::StretchY :
1259 pAny = rGeometryItem.GetPropertyValueByName( sPath, "StretchY" );
1260 if ( pAny && pDefCustomShape )
1262 sal_Int32 nStretchY = 0;
1263 if ( *pAny >>= nStretchY )
1265 if ( pDefCustomShape->nYRef == nStretchY )
1266 bIsDefaultGeometry = true;
1269 else if ( pDefCustomShape && ( pDefCustomShape->nYRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
1270 bIsDefaultGeometry = true;
1272 break;
1274 case DefaultType::Equations :
1276 pAny = rGeometryItem.GetPropertyValueByName( "Equations" );
1277 if ( pAny && pDefCustomShape && pDefCustomShape->nCalculation && pDefCustomShape->pCalculation )
1279 uno::Sequence<OUString> seqEquations1;
1280 if ( *pAny >>= seqEquations1 )
1282 sal_Int32 i, nCount = pDefCustomShape->nCalculation;
1283 uno::Sequence<OUString> seqEquations2( nCount );
1284 auto pseqEquations2 = seqEquations2.getArray();
1286 const SvxMSDffCalculationData* pData = pDefCustomShape->pCalculation;
1287 for ( i = 0; i < nCount; i++, pData++ )
1288 pseqEquations2[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
1290 if ( seqEquations1 == seqEquations2 )
1291 bIsDefaultGeometry = true;
1294 else if ( pDefCustomShape && ( ( pDefCustomShape->nCalculation == 0 ) || ( pDefCustomShape->pCalculation == nullptr ) ) )
1295 bIsDefaultGeometry = true;
1297 break;
1299 case DefaultType::TextFrames :
1301 pAny = rGeometryItem.GetPropertyValueByName( sPath, "TextFrames" );
1302 if ( pAny && pDefCustomShape && pDefCustomShape->nTextRect && pDefCustomShape->pTextRect )
1304 uno::Sequence<drawing::EnhancedCustomShapeTextFrame> seqTextFrames1;
1305 if ( *pAny >>= seqTextFrames1 )
1307 sal_Int32 i, nCount = pDefCustomShape->nTextRect;
1308 uno::Sequence<drawing::EnhancedCustomShapeTextFrame> seqTextFrames2( nCount );
1309 auto pseqTextFrames2 = seqTextFrames2.getArray();
1310 const SvxMSDffTextRectangles* pRectangles = pDefCustomShape->pTextRect;
1311 for ( i = 0; i < nCount; i++, pRectangles++ )
1313 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].TopLeft.First, pRectangles->nPairA.nValA );
1314 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
1315 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].BottomRight.First, pRectangles->nPairB.nValA );
1316 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
1318 if ( seqTextFrames1 == seqTextFrames2 )
1319 bIsDefaultGeometry = true;
1322 else if ( pDefCustomShape && ( ( pDefCustomShape->nTextRect == 0 ) || ( pDefCustomShape->pTextRect == nullptr ) ) )
1323 bIsDefaultGeometry = true;
1325 break;
1327 return bIsDefaultGeometry;
1330 void SdrObjCustomShape::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
1332 rInfo.bResizeFreeAllowed=fObjectRotation == 0.0;
1333 rInfo.bResizePropAllowed=true;
1334 rInfo.bRotateFreeAllowed=true;
1335 rInfo.bRotate90Allowed =true;
1336 rInfo.bMirrorFreeAllowed=true;
1337 rInfo.bMirror45Allowed =true;
1338 rInfo.bMirror90Allowed =true;
1339 rInfo.bTransparenceAllowed = false;
1340 rInfo.bShearAllowed =true;
1341 rInfo.bEdgeRadiusAllowed=false;
1342 rInfo.bNoContortion =true;
1344 // #i37011#
1345 if ( !mXRenderedCustomShape.is() )
1346 return;
1348 const SdrObject* pRenderedCustomShape = SdrObject::getSdrObjectFromXShape( mXRenderedCustomShape );
1349 if ( !pRenderedCustomShape )
1350 return;
1352 // #i37262#
1353 // Iterate self over the contained objects, since there are combinations of
1354 // polygon and curve objects. In that case, aInfo.bCanConvToPath and
1355 // aInfo.bCanConvToPoly would be false. What is needed here is an or, not an and.
1356 SdrObjListIter aIterator(*pRenderedCustomShape);
1357 while(aIterator.IsMore())
1359 SdrObject* pCandidate = aIterator.Next();
1360 SdrObjTransformInfoRec aInfo;
1361 pCandidate->TakeObjInfo(aInfo);
1363 // set path and poly conversion if one is possible since
1364 // this object will first be broken
1365 const bool bCanConvToPathOrPoly(aInfo.bCanConvToPath || aInfo.bCanConvToPoly);
1366 if(rInfo.bCanConvToPath != bCanConvToPathOrPoly)
1368 rInfo.bCanConvToPath = bCanConvToPathOrPoly;
1371 if(rInfo.bCanConvToPoly != bCanConvToPathOrPoly)
1373 rInfo.bCanConvToPoly = bCanConvToPathOrPoly;
1376 if(rInfo.bCanConvToContour != aInfo.bCanConvToContour)
1378 rInfo.bCanConvToContour = aInfo.bCanConvToContour;
1381 if(rInfo.bShearAllowed != aInfo.bShearAllowed)
1383 rInfo.bShearAllowed = aInfo.bShearAllowed;
1388 SdrObjKind SdrObjCustomShape::GetObjIdentifier() const
1390 return SdrObjKind::CustomShape;
1393 // #115391# This implementation is based on the TextFrame size of the CustomShape and the
1394 // state of the ResizeShapeToFitText flag to correctly set TextMinFrameWidth/Height
1395 void SdrObjCustomShape::AdaptTextMinSize()
1397 if (getSdrModelFromSdrObject().IsCreatingDataObj() || getSdrModelFromSdrObject().IsPasteResize())
1398 return;
1400 // check if we need to change anything before creating an SfxItemSet, because that is expensive
1401 const bool bResizeShapeToFitText(GetObjectItem(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue());
1402 tools::Rectangle aTextBound(getRectangle());
1403 bool bChanged(false);
1404 if(bResizeShapeToFitText)
1405 bChanged = true;
1406 else if(GetTextBounds(aTextBound))
1407 bChanged = true;
1408 if (!bChanged)
1409 return;
1411 SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
1412 SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH> // contains SDRATTR_TEXT_MAXFRAMEWIDTH
1413 aSet(*GetObjectItemSet().GetPool());
1415 if(bResizeShapeToFitText)
1417 // always reset MinWidthHeight to zero to only rely on text size and frame size
1418 // to allow resizing being completely dependent on text size only
1419 aSet.Put(makeSdrTextMinFrameWidthItem(0));
1420 aSet.Put(makeSdrTextMinFrameHeightItem(0));
1422 else
1424 // recreate from CustomShape-specific TextBounds
1425 const tools::Long nHDist(GetTextLeftDistance() + GetTextRightDistance());
1426 const tools::Long nVDist(GetTextUpperDistance() + GetTextLowerDistance());
1427 const tools::Long nTWdt(std::max(tools::Long(0), static_cast<tools::Long>(aTextBound.GetWidth() - 1 - nHDist)));
1428 const tools::Long nTHgt(std::max(tools::Long(0), static_cast<tools::Long>(aTextBound.GetHeight() - 1 - nVDist)));
1430 aSet.Put(makeSdrTextMinFrameWidthItem(nTWdt));
1431 aSet.Put(makeSdrTextMinFrameHeightItem(nTHgt));
1434 SetObjectItemSet(aSet);
1437 void SdrObjCustomShape::NbcSetSnapRect(const tools::Rectangle& rRectangle)
1439 tools::Rectangle aRectangle(rRectangle);
1440 ImpJustifyRect(aRectangle);
1441 setRectangle(aRectangle);
1442 InvalidateRenderGeometry();
1444 AdaptTextMinSize();
1446 ImpCheckShear();
1447 SetBoundAndSnapRectsDirty();
1448 SetChanged();
1451 void SdrObjCustomShape::SetSnapRect( const tools::Rectangle& rRect )
1453 tools::Rectangle aBoundRect0;
1454 if ( m_pUserCall )
1455 aBoundRect0 = GetLastBoundRect();
1456 NbcSetSnapRect( rRect );
1457 BroadcastObjectChange();
1458 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
1461 void SdrObjCustomShape::NbcSetLogicRect(const tools::Rectangle& rRectangle)
1463 tools::Rectangle aRectangle(rRectangle);
1464 ImpJustifyRect(aRectangle);
1465 setRectangle(aRectangle);
1466 InvalidateRenderGeometry();
1468 AdaptTextMinSize();
1470 SetBoundAndSnapRectsDirty();
1471 SetChanged();
1474 void SdrObjCustomShape::SetLogicRect( const tools::Rectangle& rRect )
1476 tools::Rectangle aBoundRect0;
1477 if ( m_pUserCall )
1478 aBoundRect0 = GetLastBoundRect();
1479 NbcSetLogicRect(rRect);
1480 BroadcastObjectChange();
1481 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
1484 void SdrObjCustomShape::Move( const Size& rSiz )
1486 if ( rSiz.Width() || rSiz.Height() )
1488 tools::Rectangle aBoundRect0;
1489 if ( m_pUserCall )
1490 aBoundRect0 = GetLastBoundRect();
1491 NbcMove(rSiz);
1492 SetChanged();
1493 BroadcastObjectChange();
1494 SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
1497 void SdrObjCustomShape::NbcMove( const Size& rSiz )
1499 SdrTextObj::NbcMove( rSiz );
1500 if ( mXRenderedCustomShape.is() )
1502 SdrObject* pRenderedCustomShape = SdrObject::getSdrObjectFromXShape(mXRenderedCustomShape);
1503 if ( pRenderedCustomShape )
1505 // #i97149# the visualisation shape needs to be informed
1506 // about change, too
1507 pRenderedCustomShape->ActionChanged();
1508 pRenderedCustomShape->NbcMove( rSiz );
1512 // #i37011# adapt geometry shadow
1513 if(mpLastShadowGeometry)
1515 mpLastShadowGeometry->NbcMove( rSiz );
1519 void SdrObjCustomShape::NbcResize( const Point& rRef, const Fraction& rxFact, const Fraction& ryFact )
1521 // taking care of handles that should not been changed
1522 tools::Rectangle aOld(getRectangle());
1523 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
1525 SdrTextObj::NbcResize( rRef, rxFact, ryFact );
1527 if ( ( rxFact.GetNumerator() != rxFact.GetDenominator() )
1528 || ( ryFact.GetNumerator()!= ryFact.GetDenominator() ) )
1530 if ( ( ( rxFact.GetNumerator() < 0 ) && ( rxFact.GetDenominator() > 0 ) ) ||
1531 ( ( rxFact.GetNumerator() > 0 ) && ( rxFact.GetDenominator() < 0 ) ) )
1533 SetMirroredX( !IsMirroredX() );
1535 if ( ( ( ryFact.GetNumerator() < 0 ) && ( ryFact.GetDenominator() > 0 ) ) ||
1536 ( ( ryFact.GetNumerator() > 0 ) && ( ryFact.GetDenominator() < 0 ) ) )
1538 SetMirroredY( !IsMirroredY() );
1542 for (const auto& rInteraction : aInteractionHandles)
1546 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
1547 rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
1548 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X )
1550 sal_Int32 nX = ( rInteraction.aPosition.X - aOld.Left() ) + getRectangle().Left();
1551 rInteraction.xInteraction->setControllerPosition(awt::Point(nX, rInteraction.xInteraction->getPosition().Y));
1553 else if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
1555 sal_Int32 nX = getRectangle().Right() - (aOld.Right() - rInteraction.aPosition.X);
1556 rInteraction.xInteraction->setControllerPosition(awt::Point(nX, rInteraction.xInteraction->getPosition().Y));
1558 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
1560 sal_Int32 nY = ( rInteraction.aPosition.Y - aOld.Top() ) + getRectangle().Top();
1561 rInteraction.xInteraction->setControllerPosition(awt::Point(rInteraction.xInteraction->getPosition().X, nY));
1564 catch ( const uno::RuntimeException& )
1569 // updating fObjectRotation
1570 Degree100 nTextObjRotation = maGeo.nRotationAngle;
1571 double fAngle = toDegrees(nTextObjRotation);
1572 if (IsMirroredX())
1574 if (IsMirroredY())
1575 fObjectRotation = fAngle - 180.0;
1576 else
1577 fObjectRotation = -fAngle;
1579 else
1581 if (IsMirroredY())
1582 fObjectRotation = 180.0 - fAngle;
1583 else
1584 fObjectRotation = fAngle;
1586 while (fObjectRotation < 0)
1587 fObjectRotation += 360.0;
1588 while (fObjectRotation >= 360.0)
1589 fObjectRotation -= 360.0;
1591 InvalidateRenderGeometry();
1594 void SdrObjCustomShape::NbcRotate( const Point& rRef, Degree100 nAngle, double sn, double cs )
1596 bool bMirroredX = IsMirroredX();
1597 bool bMirroredY = IsMirroredY();
1599 fObjectRotation = fmod( fObjectRotation, 360.0 );
1600 if ( fObjectRotation < 0 )
1601 fObjectRotation = 360 + fObjectRotation;
1603 // the rotation angle for ashapes is stored in fObjectRotation, this rotation
1604 // has to be applied to the text object (which is internally using maGeo.nAngle).
1605 SdrTextObj::NbcRotate( getRectangle().TopLeft(), -maGeo.nRotationAngle, // retrieving the unrotated text object
1606 -maGeo.mfSinRotationAngle,
1607 maGeo.mfCosRotationAngle );
1608 maGeo.nRotationAngle = 0_deg100; // resetting aGeo data
1609 maGeo.RecalcSinCos();
1611 Degree100 nW(static_cast<sal_Int32>( fObjectRotation * 100 )); // applying our object rotation
1612 if ( bMirroredX )
1613 nW = 36000_deg100 - nW;
1614 if ( bMirroredY )
1615 nW = 18000_deg100 - nW;
1616 nW = nW % 36000_deg100;
1617 if ( nW < 0_deg100 )
1618 nW = 36000_deg100 + nW;
1619 SdrTextObj::NbcRotate( getRectangle().TopLeft(), nW, // applying text rotation
1620 sin( toRadians(nW) ),
1621 cos( toRadians(nW) ) );
1623 int nSwap = 0;
1624 if ( bMirroredX )
1625 nSwap ^= 1;
1626 if ( bMirroredY )
1627 nSwap ^= 1;
1629 double fAngle = toDegrees(nAngle); // updating to our new object rotation
1630 fObjectRotation = fmod( nSwap ? fObjectRotation - fAngle : fObjectRotation + fAngle, 360.0 );
1631 if ( fObjectRotation < 0 )
1632 fObjectRotation = 360 + fObjectRotation;
1634 SdrTextObj::NbcRotate( rRef, nAngle, sn, cs ); // applying text rotation
1635 InvalidateRenderGeometry();
1638 void SdrObjCustomShape::NbcMirror( const Point& rRef1, const Point& rRef2 )
1640 // TTTT: Fix for old mirroring, can be removed again in aw080
1641 // storing horizontal and vertical flipping without modifying the rotate angle
1642 // decompose other flipping to rotation and MirrorX.
1643 tools::Long ndx = rRef2.X()-rRef1.X();
1644 tools::Long ndy = rRef2.Y()-rRef1.Y();
1646 if(!ndx) // MirroredX
1648 SetMirroredX(!IsMirroredX());
1649 SdrTextObj::NbcMirror( rRef1, rRef2 );
1651 else
1653 if(!ndy) // MirroredY
1655 SetMirroredY(!IsMirroredY());
1656 SdrTextObj::NbcMirror( rRef1, rRef2 );
1658 else // neither horizontal nor vertical
1660 SetMirroredX(!IsMirroredX());
1662 // call parent
1663 SdrTextObj::NbcMirror( rRef1, rRef2 );
1665 // update fObjectRotation
1666 Degree100 nTextObjRotation = maGeo.nRotationAngle;
1667 double fAngle = toDegrees(nTextObjRotation);
1669 bool bSingleFlip = (IsMirroredX()!= IsMirroredY());
1671 fObjectRotation = fmod( bSingleFlip ? -fAngle : fAngle, 360.0 );
1673 if ( fObjectRotation < 0 )
1675 fObjectRotation = 360.0 + fObjectRotation;
1680 InvalidateRenderGeometry();
1683 void SdrObjCustomShape::Shear( const Point& rRef, Degree100 nAngle, double tn, bool bVShear )
1685 SdrTextObj::Shear( rRef, nAngle, tn, bVShear );
1686 InvalidateRenderGeometry();
1688 void SdrObjCustomShape::NbcShear( const Point& rRef, Degree100 nAngle, double tn, bool bVShear )
1690 // TTTT: Fix for old mirroring, can be removed again in aw080
1691 SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
1693 // updating fObjectRotation
1694 Degree100 nTextObjRotation = maGeo.nRotationAngle;
1695 double fAngle = toDegrees(nTextObjRotation);
1696 if (IsMirroredX())
1698 if (IsMirroredY())
1699 fObjectRotation = fAngle - 180.0;
1700 else
1701 fObjectRotation = -fAngle;
1703 else
1705 if (IsMirroredY())
1706 fObjectRotation = 180.0 - fAngle;
1707 else
1708 fObjectRotation = fAngle;
1710 while (fObjectRotation < 0)
1711 fObjectRotation += 360.0;
1712 while (fObjectRotation >= 360.0)
1713 fObjectRotation -= 360.0;
1715 InvalidateRenderGeometry();
1718 SdrGluePoint SdrObjCustomShape::GetVertexGluePoint(sal_uInt16 nPosNum) const
1720 sal_Int32 nWdt = ImpGetLineWdt(); // #i25616#
1722 // #i25616#
1723 if(!LineIsOutsideGeometry())
1725 nWdt++;
1726 nWdt /= 2;
1729 Point aPt;
1730 auto aRectangle = getRectangle();
1731 switch (nPosNum)
1733 case 0: aPt = aRectangle.TopCenter(); aPt.AdjustY( -nWdt ); break;
1734 case 1: aPt = aRectangle.RightCenter(); aPt.AdjustX(nWdt ); break;
1735 case 2: aPt = aRectangle.BottomCenter(); aPt.AdjustY(nWdt ); break;
1736 case 3: aPt = aRectangle.LeftCenter(); aPt.AdjustX( -nWdt ); break;
1738 if (maGeo.nShearAngle != 0_deg100)
1739 ShearPoint(aPt, aRectangle.TopLeft(), maGeo.mfTanShearAngle);
1740 if (maGeo.nRotationAngle != 0_deg100)
1741 RotatePoint(aPt, aRectangle.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1742 aPt-=GetSnapRect().Center();
1743 SdrGluePoint aGP(aPt);
1744 aGP.SetPercent(false);
1745 return aGP;
1749 // #i38892#
1750 void SdrObjCustomShape::ImpCheckCustomGluePointsAreAdded()
1752 const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
1754 if(!pSdrObject)
1755 return;
1757 const SdrGluePointList* pSource = pSdrObject->GetGluePointList();
1759 if(!(pSource && pSource->GetCount()))
1760 return;
1762 if(!SdrTextObj::GetGluePointList())
1764 SdrTextObj::ForceGluePointList();
1767 const SdrGluePointList* pList = SdrTextObj::GetGluePointList();
1769 if(!pList)
1770 return;
1772 SdrGluePointList aNewList;
1773 sal_uInt16 a;
1775 for(a = 0; a < pSource->GetCount(); a++)
1777 SdrGluePoint aCopy((*pSource)[a]);
1778 aCopy.SetUserDefined(false);
1779 aNewList.Insert(aCopy);
1782 bool bMirroredX = IsMirroredX();
1783 bool bMirroredY = IsMirroredY();
1785 Degree100 nShearAngle = maGeo.nShearAngle;
1786 double fTan = maGeo.mfTanShearAngle;
1788 if (maGeo.nRotationAngle || nShearAngle || bMirroredX || bMirroredY)
1790 tools::Polygon aPoly(getRectangle());
1791 if( nShearAngle )
1793 sal_uInt16 nPointCount=aPoly.GetSize();
1794 for (sal_uInt16 i=0; i<nPointCount; i++)
1795 ShearPoint(aPoly[i], getRectangle().Center(), fTan );
1797 if (maGeo.nRotationAngle)
1798 aPoly.Rotate( getRectangle().Center(), to<Degree10>(maGeo.nRotationAngle) );
1800 tools::Rectangle aBoundRect( aPoly.GetBoundRect() );
1801 sal_Int32 nXDiff = aBoundRect.Left() - getRectangle().Left();
1802 sal_Int32 nYDiff = aBoundRect.Top() - getRectangle().Top();
1804 if (nShearAngle && bMirroredX != bMirroredY)
1806 nShearAngle = -nShearAngle;
1807 fTan = -fTan;
1810 Point aRef( getRectangle().GetWidth() / 2, getRectangle().GetHeight() / 2 );
1811 for ( a = 0; a < aNewList.GetCount(); a++ )
1813 SdrGluePoint& rPoint = aNewList[ a ];
1814 Point aGlue( rPoint.GetPos() );
1815 if ( nShearAngle )
1816 ShearPoint( aGlue, aRef, fTan );
1818 RotatePoint(aGlue, aRef, sin(basegfx::deg2rad(fObjectRotation)),
1819 cos(basegfx::deg2rad(fObjectRotation)));
1820 if ( bMirroredX )
1821 aGlue.setX( getRectangle().GetWidth() - aGlue.X() );
1822 if ( bMirroredY )
1823 aGlue.setY( getRectangle().GetHeight() - aGlue.Y() );
1824 aGlue.AdjustX( -nXDiff );
1825 aGlue.AdjustY( -nYDiff );
1826 rPoint.SetPos( aGlue );
1830 for(a = 0; a < pList->GetCount(); a++)
1832 const SdrGluePoint& rCandidate = (*pList)[a];
1834 if(rCandidate.IsUserDefined())
1836 aNewList.Insert(rCandidate);
1840 // copy new list to local. This is NOT very convenient behavior, the local
1841 // GluePointList should not be set, but we delivered by using GetGluePointList(),
1842 // maybe on demand. Since the local object is changed here, this is assumed to
1843 // be a result of GetGluePointList and thus the list is copied
1844 if(m_pPlusData)
1846 m_pPlusData->SetGluePoints(aNewList);
1850 // #i38892#
1851 const SdrGluePointList* SdrObjCustomShape::GetGluePointList() const
1853 const_cast<SdrObjCustomShape*>(this)->ImpCheckCustomGluePointsAreAdded();
1854 return SdrTextObj::GetGluePointList();
1857 // #i38892#
1858 SdrGluePointList* SdrObjCustomShape::ForceGluePointList()
1860 if(SdrTextObj::ForceGluePointList())
1862 ImpCheckCustomGluePointsAreAdded();
1863 return SdrTextObj::ForceGluePointList();
1865 else
1867 return nullptr;
1872 sal_uInt32 SdrObjCustomShape::GetHdlCount() const
1874 const sal_uInt32 nBasicHdlCount(SdrTextObj::GetHdlCount());
1875 return ( GetInteractionHandles().size() + nBasicHdlCount );
1878 void SdrObjCustomShape::AddToHdlList(SdrHdlList& rHdlList) const
1880 SdrTextObj::AddToHdlList(rHdlList);
1882 int nCustomShapeHdlNum = 0;
1883 for (SdrCustomShapeInteraction const & rInteraction : GetInteractionHandles())
1885 if ( rInteraction.xInteraction.is() )
1889 awt::Point aPosition( rInteraction.xInteraction->getPosition() );
1890 std::unique_ptr<SdrHdl> pH(new SdrHdl( Point( aPosition.X, aPosition.Y ), SdrHdlKind::CustomShape1 ));
1891 pH->SetPointNum( nCustomShapeHdlNum );
1892 pH->SetObj( const_cast<SdrObjCustomShape*>(this) );
1893 rHdlList.AddHdl(std::move(pH));
1895 catch ( const uno::RuntimeException& )
1899 ++nCustomShapeHdlNum;
1903 bool SdrObjCustomShape::hasSpecialDrag() const
1905 return true;
1908 bool SdrObjCustomShape::beginSpecialDrag(SdrDragStat& rDrag) const
1910 const SdrHdl* pHdl = rDrag.GetHdl();
1912 if(pHdl && SdrHdlKind::CustomShape1 == pHdl->GetKind())
1914 rDrag.SetEndDragChangesAttributes(true);
1915 rDrag.SetNoSnap();
1917 else
1919 const SdrHdl* pHdl2 = rDrag.GetHdl();
1920 const SdrHdlKind eHdl((pHdl2 == nullptr) ? SdrHdlKind::Move : pHdl2->GetKind());
1922 switch( eHdl )
1924 case SdrHdlKind::UpperLeft :
1925 case SdrHdlKind::Upper :
1926 case SdrHdlKind::UpperRight :
1927 case SdrHdlKind::Left :
1928 case SdrHdlKind::Right :
1929 case SdrHdlKind::LowerLeft :
1930 case SdrHdlKind::Lower :
1931 case SdrHdlKind::LowerRight :
1932 case SdrHdlKind::Move :
1934 break;
1936 default:
1938 return false;
1943 return true;
1946 void SdrObjCustomShape::DragResizeCustomShape( const tools::Rectangle& rNewRect )
1948 tools::Rectangle aOld(getRectangle());
1949 bool bOldMirroredX( IsMirroredX() );
1950 bool bOldMirroredY( IsMirroredY() );
1952 tools::Rectangle aNewRect( rNewRect );
1953 aNewRect.Normalize();
1955 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
1957 GeoStat aGeoStat( GetGeoStat() );
1958 if ( aNewRect.TopLeft() != getRectangle().TopLeft() &&
1959 ( maGeo.nRotationAngle || maGeo.nShearAngle ) )
1961 Point aNewPos( aNewRect.TopLeft() );
1962 if ( maGeo.nShearAngle ) ShearPoint( aNewPos, aOld.TopLeft(), aGeoStat.mfTanShearAngle );
1963 if ( maGeo.nRotationAngle ) RotatePoint(aNewPos, aOld.TopLeft(), aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle );
1964 aNewRect.SetPos( aNewPos );
1966 if (aNewRect == getRectangle())
1967 return;
1969 SetLogicRect( aNewRect );
1970 InvalidateRenderGeometry();
1972 if ( rNewRect.Left() > rNewRect.Right() )
1974 Point aTop( ( GetSnapRect().Left() + GetSnapRect().Right() ) >> 1, GetSnapRect().Top() );
1975 Point aBottom( aTop.X(), aTop.Y() + 1000 );
1976 NbcMirror( aTop, aBottom );
1978 if ( rNewRect.Top() > rNewRect.Bottom() )
1980 Point aLeft( GetSnapRect().Left(), ( GetSnapRect().Top() + GetSnapRect().Bottom() ) >> 1 );
1981 Point aRight( aLeft.X() + 1000, aLeft.Y() );
1982 NbcMirror( aLeft, aRight );
1985 for (const auto& rInteraction : aInteractionHandles)
1989 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
1990 rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
1991 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X ||
1992 rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
1994 if (rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX)
1995 bOldMirroredX = !bOldMirroredX;
1997 sal_Int32 nX;
1998 if ( bOldMirroredX )
2000 nX = ( rInteraction.aPosition.X - aOld.Right() );
2001 if ( rNewRect.Left() > rNewRect.Right() )
2002 nX = getRectangle().Left() - nX;
2003 else
2004 nX += getRectangle().Right();
2006 else
2008 nX = ( rInteraction.aPosition.X - aOld.Left() );
2009 if ( rNewRect.Left() > rNewRect.Right() )
2010 nX = getRectangle().Right() - nX;
2011 else
2012 nX += getRectangle().Left();
2014 rInteraction.xInteraction->setControllerPosition(awt::Point(nX, rInteraction.xInteraction->getPosition().Y));
2016 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
2018 sal_Int32 nY;
2019 if ( bOldMirroredY )
2021 nY = ( rInteraction.aPosition.Y - aOld.Bottom() );
2022 if ( rNewRect.Top() > rNewRect.Bottom() )
2023 nY = getRectangle().Top() - nY;
2024 else
2025 nY += getRectangle().Bottom();
2027 else
2029 nY = ( rInteraction.aPosition.Y - aOld.Top() );
2030 if ( rNewRect.Top() > rNewRect.Bottom() )
2031 nY = getRectangle().Bottom() - nY;
2032 else
2033 nY += getRectangle().Top();
2035 rInteraction.xInteraction->setControllerPosition(awt::Point(rInteraction.xInteraction->getPosition().X, nY));
2038 catch ( const uno::RuntimeException& )
2044 void SdrObjCustomShape::DragMoveCustomShapeHdl( const Point& rDestination,
2045 const sal_uInt16 nCustomShapeHdlNum, bool bMoveCalloutRectangle )
2047 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
2048 if ( nCustomShapeHdlNum >= aInteractionHandles.size() )
2049 return;
2051 SdrCustomShapeInteraction aInteractionHandle( aInteractionHandles[ nCustomShapeHdlNum ] );
2052 if ( !aInteractionHandle.xInteraction.is() )
2053 return;
2057 awt::Point aPt( rDestination.X(), rDestination.Y() );
2058 if ( aInteractionHandle.nMode & CustomShapeHandleModes::MOVE_SHAPE && bMoveCalloutRectangle )
2060 sal_Int32 nXDiff = aPt.X - aInteractionHandle.aPosition.X;
2061 sal_Int32 nYDiff = aPt.Y - aInteractionHandle.aPosition.Y;
2063 moveRectangle(nXDiff, nYDiff);
2064 moveOutRectangle(nXDiff, nYDiff);
2065 maSnapRect.Move( nXDiff, nYDiff );
2066 SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
2067 InvalidateRenderGeometry();
2069 for (const auto& rInteraction : aInteractionHandles)
2071 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
2073 if ( rInteraction.xInteraction.is() )
2074 rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
2078 aInteractionHandle.xInteraction->setControllerPosition( aPt );
2080 catch ( const uno::RuntimeException& )
2085 bool SdrObjCustomShape::applySpecialDrag(SdrDragStat& rDrag)
2087 const SdrHdl* pHdl = rDrag.GetHdl();
2088 const SdrHdlKind eHdl((pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind());
2090 switch(eHdl)
2092 case SdrHdlKind::CustomShape1 :
2094 rDrag.SetEndDragChangesGeoAndAttributes(true);
2095 DragMoveCustomShapeHdl( rDrag.GetNow(), static_cast<sal_uInt16>(pHdl->GetPointNum()), !rDrag.GetDragMethod()->IsShiftPressed() );
2096 SetBoundAndSnapRectsDirty();
2097 InvalidateRenderGeometry();
2098 SetChanged();
2099 break;
2102 case SdrHdlKind::UpperLeft :
2103 case SdrHdlKind::Upper :
2104 case SdrHdlKind::UpperRight :
2105 case SdrHdlKind::Left :
2106 case SdrHdlKind::Right :
2107 case SdrHdlKind::LowerLeft :
2108 case SdrHdlKind::Lower :
2109 case SdrHdlKind::LowerRight :
2111 DragResizeCustomShape( ImpDragCalcRect(rDrag) );
2112 break;
2114 case SdrHdlKind::Move :
2116 Move(Size(rDrag.GetDX(), rDrag.GetDY()));
2117 break;
2119 default: break;
2122 return true;
2126 void SdrObjCustomShape::DragCreateObject( SdrDragStat& rStat )
2128 tools::Rectangle aRect1;
2129 rStat.TakeCreateRect( aRect1 );
2131 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
2133 constexpr sal_uInt32 nDefaultObjectSizeWidth = 3000; // default width from SDOptions ?
2134 constexpr sal_uInt32 nDefaultObjectSizeHeight= 3000;
2136 if ( ImpVerticalSwitch( *this ) )
2138 SetMirroredX( aRect1.Left() > aRect1.Right() );
2140 aRect1 = tools::Rectangle( rStat.GetNow(), Size( nDefaultObjectSizeWidth, nDefaultObjectSizeHeight ) );
2141 // subtracting the horizontal difference of the latest handle from shape position
2142 if ( !aInteractionHandles.empty() )
2144 sal_Int32 nHandlePos = aInteractionHandles[ aInteractionHandles.size() - 1 ].xInteraction->getPosition().X;
2145 aRect1.Move(getRectangle().Left() - nHandlePos, 0);
2148 ImpJustifyRect( aRect1 );
2149 rStat.SetActionRect( aRect1 );
2150 setRectangle(aRect1);
2151 SetBoundAndSnapRectsDirty();
2153 for (const auto& rInteraction : aInteractionHandles)
2157 if ( rInteraction.nMode & CustomShapeHandleModes::CREATE_FIXED )
2158 rInteraction.xInteraction->setControllerPosition( awt::Point( rStat.GetStart().X(), rStat.GetStart().Y() ) );
2160 catch ( const uno::RuntimeException& )
2165 SetBoundRectDirty();
2166 m_bSnapRectDirty=true;
2169 bool SdrObjCustomShape::MovCreate(SdrDragStat& rStat)
2171 SdrView* pView = rStat.GetView(); // #i37448#
2172 if( pView && pView->IsSolidDragging() )
2174 InvalidateRenderGeometry();
2176 DragCreateObject( rStat );
2177 SetBoundAndSnapRectsDirty();
2178 return true;
2181 bool SdrObjCustomShape::EndCreate( SdrDragStat& rStat, SdrCreateCmd eCmd )
2183 DragCreateObject( rStat );
2185 AdaptTextMinSize();
2187 SetBoundAndSnapRectsDirty();
2188 return ( eCmd == SdrCreateCmd::ForceEnd || rStat.GetPointCount() >= 2 );
2191 basegfx::B2DPolyPolygon SdrObjCustomShape::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
2193 return GetLineGeometry( false );
2197 // in context with the SdrObjCustomShape the SdrTextAutoGrowHeightItem == true -> Resize Shape to fit text,
2198 // the SdrTextAutoGrowWidthItem == true -> Word wrap text in Shape
2199 bool SdrObjCustomShape::IsAutoGrowHeight() const
2201 const SfxItemSet& rSet = GetMergedItemSet();
2202 bool bIsAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
2203 if ( bIsAutoGrowHeight && IsVerticalWriting() )
2204 bIsAutoGrowHeight = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue();
2205 return bIsAutoGrowHeight;
2207 bool SdrObjCustomShape::IsAutoGrowWidth() const
2209 const SfxItemSet& rSet = GetMergedItemSet();
2210 bool bIsAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
2211 if ( bIsAutoGrowWidth && !IsVerticalWriting() )
2212 bIsAutoGrowWidth = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue();
2213 return bIsAutoGrowWidth;
2216 /* The following method is identical to the SdrTextObj::SetVerticalWriting method, the only difference
2217 is that the SdrAutoGrowWidthItem and SdrAutoGrowHeightItem are not exchanged if the vertical writing
2218 mode has been changed */
2220 void SdrObjCustomShape::SetVerticalWriting( bool bVertical )
2222 ForceOutlinerParaObject();
2224 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
2226 DBG_ASSERT( pOutlinerParaObject, "SdrTextObj::SetVerticalWriting() without OutlinerParaObject!" );
2228 if( !pOutlinerParaObject ||
2229 (pOutlinerParaObject->IsEffectivelyVertical() == bVertical) )
2230 return;
2232 // get item settings
2233 const SfxItemSet& rSet = GetObjectItemSet();
2235 // Also exchange horizontal and vertical adjust items
2236 SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
2237 SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
2239 // rescue object size, SetSnapRect below expects logic rect,
2240 // not snap rect.
2241 tools::Rectangle aObjectRect = GetLogicRect();
2243 // prepare ItemSet to set exchanged width and height items
2244 SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
2245 // Expanded item ranges to also support horizontal and vertical adjust.
2246 SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
2247 SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST> aNewSet(*rSet.GetPool());
2249 aNewSet.Put(rSet);
2251 // Exchange horizontal and vertical adjusts
2252 switch(eVert)
2254 case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
2255 case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
2256 case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
2257 case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
2259 switch(eHorz)
2261 case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
2262 case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
2263 case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
2264 case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
2267 pOutlinerParaObject = GetOutlinerParaObject();
2268 if ( pOutlinerParaObject )
2269 pOutlinerParaObject->SetVertical(bVertical);
2270 SetObjectItemSet( aNewSet );
2272 // restore object size
2273 SetSnapRect(aObjectRect);
2276 void SdrObjCustomShape::SuggestTextFrameSize(Size aSuggestedTextFrameSize)
2278 m_aSuggestedTextFrameSize = aSuggestedTextFrameSize;
2281 bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight(tools::Rectangle& rR, bool bHgt, bool bWdt) const
2283 // Either we have text or the application has native text and suggested its size to us.
2284 bool bHasText = HasText() || !m_aSuggestedTextFrameSize.IsEmpty();
2285 if ( bHasText && !rR.IsEmpty() )
2287 bool bWdtGrow=bWdt && IsAutoGrowWidth();
2288 bool bHgtGrow=bHgt && IsAutoGrowHeight();
2289 if ( bWdtGrow || bHgtGrow )
2291 tools::Rectangle aR0(rR);
2292 tools::Long nHgt=0,nMinHgt=0,nMaxHgt=0;
2293 tools::Long nWdt=0,nMinWdt=0,nMaxWdt=0;
2294 Size aSiz(rR.GetSize()); aSiz.AdjustWidth( -1 ); aSiz.AdjustHeight( -1 );
2295 Size aMaxSiz(100000,100000);
2296 Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
2297 if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
2298 if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
2299 if (bWdtGrow)
2301 nMinWdt=GetMinTextFrameWidth();
2302 nMaxWdt=GetMaxTextFrameWidth();
2303 if (nMaxWdt==0 || nMaxWdt>aMaxSiz.Width()) nMaxWdt=aMaxSiz.Width();
2304 if (nMinWdt<=0) nMinWdt=1;
2305 aSiz.setWidth(nMaxWdt );
2307 if (bHgtGrow)
2309 nMinHgt=GetMinTextFrameHeight();
2310 nMaxHgt=GetMaxTextFrameHeight();
2311 if (nMaxHgt==0 || nMaxHgt>aMaxSiz.Height()) nMaxHgt=aMaxSiz.Height();
2312 if (nMinHgt<=0) nMinHgt=1;
2313 aSiz.setHeight(nMaxHgt );
2315 tools::Long nHDist=GetTextLeftDistance()+GetTextRightDistance();
2316 tools::Long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
2317 aSiz.AdjustWidth( -nHDist );
2318 aSiz.AdjustHeight( -nVDist );
2319 if ( aSiz.Width() < 2 )
2320 aSiz.setWidth( 2 ); // minimum size=2
2321 if ( aSiz.Height() < 2 )
2322 aSiz.setHeight( 2 ); // minimum size=2
2324 if (HasText())
2326 if(mpEditingOutliner)
2328 mpEditingOutliner->SetMaxAutoPaperSize( aSiz );
2329 if (bWdtGrow)
2331 Size aSiz2(mpEditingOutliner->CalcTextSize());
2332 nWdt=aSiz2.Width()+1; // a little more tolerance
2333 if (bHgtGrow) nHgt=aSiz2.Height()+1; // a little more tolerance
2334 } else
2336 nHgt=mpEditingOutliner->GetTextHeight()+1; // a little more tolerance
2339 else
2341 Outliner& rOutliner=ImpGetDrawOutliner();
2342 rOutliner.SetPaperSize(aSiz);
2343 rOutliner.SetUpdateLayout(true);
2344 // TODO: add the optimization with bPortionInfoChecked again.
2345 OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
2346 if( pOutlinerParaObject != nullptr )
2348 rOutliner.SetText(*pOutlinerParaObject);
2349 rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
2351 if ( bWdtGrow )
2353 Size aSiz2(rOutliner.CalcTextSize());
2354 nWdt=aSiz2.Width()+1; // a little more tolerance
2355 if ( bHgtGrow )
2356 nHgt=aSiz2.Height()+1; // a little more tolerance
2358 else
2360 nHgt = rOutliner.GetTextHeight()+1; // a little more tolerance
2362 sal_Int16 nColumns = GetMergedItem(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue();
2363 if (bHgtGrow && nColumns > 1)
2365 // Both 'resize shape to fix text' and multiple columns are enabled. The
2366 // first means a dynamic height, the second expects a fixed height.
2367 // Resolve this conflict by going with the original height.
2368 nHgt = rR.getOpenHeight();
2371 rOutliner.Clear();
2374 else
2376 nHgt = m_aSuggestedTextFrameSize.Height();
2377 nWdt = m_aSuggestedTextFrameSize.Width();
2379 if ( nWdt < nMinWdt )
2380 nWdt = nMinWdt;
2381 if ( nWdt > nMaxWdt )
2382 nWdt = nMaxWdt;
2383 nWdt += nHDist;
2384 if ( nWdt < 1 )
2385 nWdt = 1; // nHDist may also be negative
2386 if ( nHgt < nMinHgt )
2387 nHgt = nMinHgt;
2388 if ( nHgt > nMaxHgt )
2389 nHgt = nMaxHgt;
2390 nHgt+=nVDist;
2391 if ( nHgt < 1 )
2392 nHgt = 1; // nVDist may also be negative
2393 tools::Long nWdtGrow = nWdt-(rR.Right()-rR.Left());
2394 tools::Long nHgtGrow = nHgt-(rR.Bottom()-rR.Top());
2395 if ( nWdtGrow == 0 )
2396 bWdtGrow = false;
2397 if ( nHgtGrow == 0 )
2398 bHgtGrow=false;
2399 if ( bWdtGrow || bHgtGrow || !m_aSuggestedTextFrameSize.IsEmpty())
2401 if ( bWdtGrow || m_aSuggestedTextFrameSize.Width() )
2403 SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
2404 if (m_aSuggestedTextFrameSize.Width())
2406 rR.SetRight(rR.Left() + m_aSuggestedTextFrameSize.Width());
2408 else if ( eHAdj == SDRTEXTHORZADJUST_LEFT )
2409 rR.AdjustRight(nWdtGrow );
2410 else if ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
2411 rR.AdjustLeft( -nWdtGrow );
2412 else
2414 tools::Long nWdtGrow2=nWdtGrow/2;
2415 rR.AdjustLeft( -nWdtGrow2 );
2416 rR.SetRight(rR.Left()+nWdt );
2419 if ( bHgtGrow || m_aSuggestedTextFrameSize.Height() )
2421 SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
2422 if (m_aSuggestedTextFrameSize.Height())
2424 rR.SetBottom(rR.Top() + m_aSuggestedTextFrameSize.Height());
2426 else if ( eVAdj == SDRTEXTVERTADJUST_TOP )
2427 rR.AdjustBottom(nHgtGrow );
2428 else if ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
2429 rR.AdjustTop( -nHgtGrow );
2430 else
2432 tools::Long nHgtGrow2=nHgtGrow/2;
2433 rR.AdjustTop( -nHgtGrow2 );
2434 rR.SetBottom(rR.Top()+nHgt );
2437 if ( maGeo.nRotationAngle )
2439 Point aD1(rR.TopLeft());
2440 aD1-=aR0.TopLeft();
2441 Point aD2(aD1);
2442 RotatePoint(aD2,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
2443 aD2-=aD1;
2444 rR.Move(aD2.X(),aD2.Y());
2446 return true;
2450 return false;
2453 tools::Rectangle SdrObjCustomShape::ImpCalculateTextFrame( const bool bHgt, const bool bWdt )
2455 tools::Rectangle aReturnValue;
2457 tools::Rectangle aOldTextRect(getRectangle()); // <- initial text rectangle
2459 tools::Rectangle aNewTextRect(getRectangle()); // <- new text rectangle returned from the custom shape renderer,
2460 GetTextBounds( aNewTextRect ); // it depends to the current logical shape size
2462 tools::Rectangle aAdjustedTextRect( aNewTextRect ); // <- new text rectangle is being tested by AdjustTextFrameWidthAndHeight to ensure
2463 if ( AdjustTextFrameWidthAndHeight( aAdjustedTextRect, bHgt, bWdt ) ) // that the new text rectangle is matching the current text size from the outliner
2465 if (aAdjustedTextRect != aNewTextRect && aOldTextRect != aAdjustedTextRect &&
2466 aNewTextRect.GetWidth() && aNewTextRect.GetHeight())
2468 aReturnValue = getRectangle();
2469 double fXScale = static_cast<double>(aOldTextRect.GetWidth()) / static_cast<double>(aNewTextRect.GetWidth());
2470 double fYScale = static_cast<double>(aOldTextRect.GetHeight()) / static_cast<double>(aNewTextRect.GetHeight());
2471 double fRightDiff = static_cast<double>( aAdjustedTextRect.Right() - aNewTextRect.Right() ) * fXScale;
2472 double fLeftDiff = static_cast<double>( aAdjustedTextRect.Left() - aNewTextRect.Left() ) * fXScale;
2473 double fTopDiff = static_cast<double>( aAdjustedTextRect.Top() - aNewTextRect.Top() ) * fYScale;
2474 double fBottomDiff= static_cast<double>( aAdjustedTextRect.Bottom()- aNewTextRect.Bottom()) * fYScale;
2475 aReturnValue.AdjustLeft(static_cast<sal_Int32>(fLeftDiff) );
2476 aReturnValue.AdjustRight(static_cast<sal_Int32>(fRightDiff) );
2477 aReturnValue.AdjustTop(static_cast<sal_Int32>(fTopDiff) );
2478 aReturnValue.AdjustBottom(static_cast<sal_Int32>(fBottomDiff) );
2481 return aReturnValue;
2484 bool SdrObjCustomShape::NbcAdjustTextFrameWidthAndHeight(bool bHgt, bool bWdt)
2486 tools::Rectangle aNewTextRect = ImpCalculateTextFrame(bHgt, bWdt);
2487 const bool bRet = !aNewTextRect.IsEmpty() && aNewTextRect != getRectangle();
2488 if (bRet && !mbAdjustingTextFrameWidthAndHeight)
2490 mbAdjustingTextFrameWidthAndHeight = true;
2492 // taking care of handles that should not been changed
2493 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
2495 setRectangle(aNewTextRect);
2496 SetBoundAndSnapRectsDirty();
2497 SetChanged();
2499 for (const auto& rInteraction : aInteractionHandles)
2503 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
2504 rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
2506 catch ( const uno::RuntimeException& )
2510 InvalidateRenderGeometry();
2512 mbAdjustingTextFrameWidthAndHeight = false;
2514 return bRet;
2517 bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight()
2519 tools::Rectangle aNewTextRect = ImpCalculateTextFrame( true/*bHgt*/, true/*bWdt*/ );
2520 bool bRet = !aNewTextRect.IsEmpty() && ( aNewTextRect != getRectangle());
2521 if ( bRet )
2523 tools::Rectangle aBoundRect0;
2524 if ( m_pUserCall )
2525 aBoundRect0 = GetCurrentBoundRect();
2527 // taking care of handles that should not been changed
2528 std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
2530 setRectangle(aNewTextRect);
2531 SetBoundAndSnapRectsDirty();
2533 for (const auto& rInteraction : aInteractionHandles)
2537 if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
2538 rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
2540 catch ( const uno::RuntimeException& )
2545 InvalidateRenderGeometry();
2546 SetChanged();
2547 BroadcastObjectChange();
2548 SendUserCall(SdrUserCallType::Resize,aBoundRect0);
2550 return bRet;
2552 void SdrObjCustomShape::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
2554 tools::Rectangle aViewInit;
2555 TakeTextAnchorRect( aViewInit );
2556 if (maGeo.nRotationAngle)
2558 Point aCenter(aViewInit.Center());
2559 aCenter-=aViewInit.TopLeft();
2560 Point aCenter0(aCenter);
2561 RotatePoint(aCenter, Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
2562 aCenter-=aCenter0;
2563 aViewInit.Move(aCenter.X(),aCenter.Y());
2565 Size aAnkSiz(aViewInit.GetSize());
2566 aAnkSiz.AdjustWidth( -1 ); aAnkSiz.AdjustHeight( -1 ); // because GetSize() adds 1
2567 Size aMaxSiz(1000000,1000000);
2569 Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
2570 if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
2571 if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
2573 SdrTextHorzAdjust eHAdj(GetTextHorizontalAdjust());
2574 SdrTextVertAdjust eVAdj(GetTextVerticalAdjust());
2576 tools::Long nMinWdt = GetMinTextFrameWidth();
2577 tools::Long nMinHgt = GetMinTextFrameHeight();
2578 tools::Long nMaxWdt = GetMaxTextFrameWidth();
2579 tools::Long nMaxHgt = GetMaxTextFrameHeight();
2580 if (nMinWdt<1) nMinWdt=1;
2581 if (nMinHgt<1) nMinHgt=1;
2582 if ( nMaxWdt == 0 || nMaxWdt > aMaxSiz.Width() )
2583 nMaxWdt = aMaxSiz.Width();
2584 if ( nMaxHgt == 0 || nMaxHgt > aMaxSiz.Height() )
2585 nMaxHgt=aMaxSiz.Height();
2587 if (GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue())
2589 if ( IsVerticalWriting() )
2591 nMaxHgt = aAnkSiz.Height();
2592 nMinHgt = nMaxHgt;
2594 else
2596 nMaxWdt = aAnkSiz.Width();
2597 nMinWdt = nMaxWdt;
2600 Size aPaperMax(nMaxWdt, nMaxHgt);
2601 Size aPaperMin(nMinWdt, nMinHgt);
2603 if ( pViewMin )
2605 *pViewMin = aViewInit;
2607 tools::Long nXFree = aAnkSiz.Width() - aPaperMin.Width();
2608 if ( eHAdj == SDRTEXTHORZADJUST_LEFT )
2609 pViewMin->AdjustRight( -nXFree );
2610 else if ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
2611 pViewMin->AdjustLeft(nXFree );
2612 else { pViewMin->AdjustLeft(nXFree / 2 ); pViewMin->SetRight( pViewMin->Left() + aPaperMin.Width() ); }
2614 tools::Long nYFree = aAnkSiz.Height() - aPaperMin.Height();
2615 if ( eVAdj == SDRTEXTVERTADJUST_TOP )
2616 pViewMin->AdjustBottom( -nYFree );
2617 else if ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
2618 pViewMin->AdjustTop(nYFree );
2619 else { pViewMin->AdjustTop(nYFree / 2 ); pViewMin->SetBottom( pViewMin->Top() + aPaperMin.Height() ); }
2622 if( IsVerticalWriting() )
2623 aPaperMin.setWidth( 0 );
2624 else
2625 aPaperMin.setHeight( 0 );
2627 if( eHAdj != SDRTEXTHORZADJUST_BLOCK )
2628 aPaperMin.setWidth(0 );
2630 // For complete vertical adjust support, set paper min height to 0, here.
2631 if(SDRTEXTVERTADJUST_BLOCK != eVAdj )
2632 aPaperMin.setHeight( 0 );
2634 if (pPaperMin!=nullptr) *pPaperMin=aPaperMin;
2635 if (pPaperMax!=nullptr) *pPaperMax=aPaperMax;
2636 if (pViewInit!=nullptr) *pViewInit=aViewInit;
2638 void SdrObjCustomShape::EndTextEdit( SdrOutliner& rOutl )
2640 SdrTextObj::EndTextEdit( rOutl );
2641 InvalidateRenderGeometry();
2643 void SdrObjCustomShape::TakeTextAnchorRect( tools::Rectangle& rAnchorRect ) const
2645 if ( GetTextBounds( rAnchorRect ) )
2647 Point aRotateRef( maSnapRect.Center() );
2648 AdjustRectToTextDistance(rAnchorRect);
2650 if ( rAnchorRect.GetWidth() < 2 )
2651 rAnchorRect.SetRight( rAnchorRect.Left() + 1 ); // minimal width is 2
2652 if ( rAnchorRect.GetHeight() < 2 )
2653 rAnchorRect.SetBottom( rAnchorRect.Top() + 1 ); // minimal height is 2
2654 if (maGeo.nRotationAngle)
2656 Point aP( rAnchorRect.TopLeft() );
2657 RotatePoint(aP, aRotateRef, maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
2658 rAnchorRect.SetPos( aP );
2661 else
2662 SdrTextObj::TakeTextAnchorRect( rAnchorRect );
2664 void SdrObjCustomShape::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
2665 tools::Rectangle* pAnchorRect, bool /*bLineWidth*/) const
2667 tools::Rectangle aAnkRect; // Rect in which we anchor
2668 TakeTextAnchorRect(aAnkRect);
2669 SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
2670 SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
2671 EEControlBits nStat0=rOutliner.GetControlWord();
2672 Size aNullSize;
2674 rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
2675 rOutliner.SetMinAutoPaperSize(aNullSize);
2676 sal_Int32 nMaxAutoPaperWidth = 1000000;
2677 sal_Int32 nMaxAutoPaperHeight= 1000000;
2679 tools::Long nAnkWdt=aAnkRect.GetWidth();
2680 tools::Long nAnkHgt=aAnkRect.GetHeight();
2682 if (GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue())
2684 if ( IsVerticalWriting() )
2685 nMaxAutoPaperHeight = nAnkHgt;
2686 else
2687 nMaxAutoPaperWidth = nAnkWdt;
2689 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
2691 rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
2694 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
2696 rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
2698 rOutliner.SetMaxAutoPaperSize( Size( nMaxAutoPaperWidth, nMaxAutoPaperHeight ) );
2699 rOutliner.SetPaperSize( aNullSize );
2701 // put text into the Outliner - if necessary the use the text from the EditOutliner
2702 std::optional<OutlinerParaObject> pPara;
2703 if (GetOutlinerParaObject())
2704 pPara = *GetOutlinerParaObject();
2705 if (mpEditingOutliner && !bNoEditText)
2706 pPara=mpEditingOutliner->CreateParaObject();
2708 if (pPara)
2710 bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
2711 const SdrTextObj* pTestObj = rOutliner.GetTextObj();
2713 if( !pTestObj || !bHitTest || pTestObj != this ||
2714 pTestObj->GetOutlinerParaObject() != GetOutlinerParaObject() )
2716 if( bHitTest )
2717 rOutliner.SetTextObj( this );
2719 rOutliner.SetUpdateLayout(true);
2720 rOutliner.SetText(*pPara);
2723 else
2725 rOutliner.SetTextObj( nullptr );
2728 rOutliner.SetUpdateLayout(true);
2729 rOutliner.SetControlWord(nStat0);
2731 SdrText* pText = getActiveText();
2732 if( pText )
2733 pText->CheckPortionInfo( rOutliner );
2735 Point aTextPos(aAnkRect.TopLeft());
2736 Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() has a little added tolerance, no?
2738 // For draw objects containing text correct horizontal/vertical alignment if text is bigger
2739 // than the object itself. Without that correction, the text would always be
2740 // formatted to the left edge (or top edge when vertical) of the draw object.
2742 if( !IsTextFrame() )
2744 if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
2746 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
2747 // else the alignment is wanted.
2748 if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
2750 SvxAdjust eAdjust = GetObjectItemSet().Get(EE_PARA_JUST).GetAdjust();
2751 switch (eAdjust)
2753 case SvxAdjust::Left: eHAdj = SDRTEXTHORZADJUST_LEFT; break;
2754 case SvxAdjust::Right: eHAdj = SDRTEXTHORZADJUST_RIGHT; break;
2755 case SvxAdjust::Center: eHAdj = SDRTEXTHORZADJUST_CENTER; break;
2756 default: break;
2761 if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
2763 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
2764 // else the alignment is wanted.
2765 if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
2767 eVAdj = SDRTEXTVERTADJUST_CENTER;
2772 if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
2774 tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
2775 if (eHAdj==SDRTEXTHORZADJUST_CENTER)
2776 aTextPos.AdjustX(nFreeWdt/2 );
2777 if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
2778 aTextPos.AdjustX(nFreeWdt );
2780 if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
2782 tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
2783 if (eVAdj==SDRTEXTVERTADJUST_CENTER)
2784 aTextPos.AdjustY(nFreeHgt/2 );
2785 if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
2786 aTextPos.AdjustY(nFreeHgt );
2788 if (maGeo.nRotationAngle != 0_deg100)
2789 RotatePoint(aTextPos,aAnkRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
2791 if (pAnchorRect)
2792 *pAnchorRect=aAnkRect;
2794 // using rTextRect together with ContourFrame doesn't always work correctly
2795 rTextRect=tools::Rectangle(aTextPos,aTextSiz);
2798 void SdrObjCustomShape::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject)
2800 SdrTextObj::NbcSetOutlinerParaObject( std::move(pTextObject) );
2801 SetBoundRectDirty();
2802 SetBoundAndSnapRectsDirty(true);
2803 InvalidateRenderGeometry();
2806 rtl::Reference<SdrObject> SdrObjCustomShape::CloneSdrObject(SdrModel& rTargetModel) const
2808 return new SdrObjCustomShape(rTargetModel, *this);
2811 OUString SdrObjCustomShape::TakeObjNameSingul() const
2813 OUString sName(SvxResId(STR_ObjNameSingulCUSTOMSHAPE));
2814 OUString aNm(GetName());
2815 if (!aNm.isEmpty())
2816 sName += " '" + aNm + "'";
2817 return sName;
2820 OUString SdrObjCustomShape::TakeObjNamePlural() const
2822 return SvxResId(STR_ObjNamePluralCUSTOMSHAPE);
2825 basegfx::B2DPolyPolygon SdrObjCustomShape::TakeXorPoly() const
2827 return GetLineGeometry( false );
2830 basegfx::B2DPolyPolygon SdrObjCustomShape::TakeContour() const
2832 const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
2833 if ( pSdrObject )
2834 return pSdrObject->TakeContour();
2835 return basegfx::B2DPolyPolygon();
2838 rtl::Reference<SdrObject> SdrObjCustomShape::DoConvertToPolyObj(bool bBezier, bool bAddText) const
2840 // #i37011#
2841 rtl::Reference<SdrObject> pRetval;
2842 SdrObject* pRenderedCustomShape = nullptr;
2844 if ( !mXRenderedCustomShape.is() )
2846 // force CustomShape
2847 GetSdrObjectFromCustomShape();
2850 if ( mXRenderedCustomShape.is() )
2852 pRenderedCustomShape = SdrObject::getSdrObjectFromXShape(mXRenderedCustomShape);
2855 if ( pRenderedCustomShape )
2857 // Clone to same SdrModel
2858 rtl::Reference<SdrObject> pCandidate(pRenderedCustomShape->CloneSdrObject(pRenderedCustomShape->getSdrModelFromSdrObject()));
2859 DBG_ASSERT(pCandidate, "SdrObjCustomShape::DoConvertToPolyObj: Could not clone SdrObject (!)");
2860 pRetval = pCandidate->DoConvertToPolyObj(bBezier, bAddText);
2861 pCandidate.clear();
2863 if(pRetval)
2865 const bool bShadow(GetMergedItem(SDRATTR_SHADOW).GetValue());
2866 if(bShadow)
2868 pRetval->SetMergedItem(makeSdrShadowItem(true));
2872 if(bAddText && HasText() && !IsTextPath())
2874 pRetval = ImpConvertAddText(std::move(pRetval), bBezier);
2878 return pRetval;
2881 void SdrObjCustomShape::InternalSetStyleSheet( SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, bool bBroadcast )
2883 // #i40944#
2884 InvalidateRenderGeometry();
2885 SdrObject::InternalSetStyleSheet( pNewStyleSheet, bDontRemoveHardAttr, bBroadcast );
2888 void SdrObjCustomShape::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
2890 // call parent
2891 SdrTextObj::handlePageChange(pOldPage, pNewPage);
2893 if(nullptr != pNewPage)
2895 // invalidating rectangles by SetRectsDirty is not sufficient,
2896 // AdjustTextFrameWidthAndHeight() also has to be made, both
2897 // actions are done by NbcSetSnapRect
2898 tools::Rectangle aRectangle(getRectangle()); //creating temporary rectangle #i61108#
2899 NbcSetSnapRect(aRectangle);
2903 std::unique_ptr<SdrObjGeoData> SdrObjCustomShape::NewGeoData() const
2905 return std::make_unique<SdrAShapeObjGeoData>();
2908 void SdrObjCustomShape::SaveGeoData(SdrObjGeoData& rGeo) const
2910 SdrTextObj::SaveGeoData( rGeo );
2911 SdrAShapeObjGeoData& rAGeo=static_cast<SdrAShapeObjGeoData&>(rGeo);
2912 rAGeo.fObjectRotation = fObjectRotation;
2913 rAGeo.bMirroredX = IsMirroredX();
2914 rAGeo.bMirroredY = IsMirroredY();
2916 const uno::Any* pAny = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ).GetPropertyValueByName( "AdjustmentValues" );
2917 if ( pAny )
2918 *pAny >>= rAGeo.aAdjustmentSeq;
2921 void SdrObjCustomShape::RestoreGeoData(const SdrObjGeoData& rGeo)
2923 SdrTextObj::RestoreGeoData( rGeo );
2924 const SdrAShapeObjGeoData& rAGeo=static_cast<const SdrAShapeObjGeoData&>(rGeo);
2925 fObjectRotation = rAGeo.fObjectRotation;
2926 SetMirroredX( rAGeo.bMirroredX );
2927 SetMirroredY( rAGeo.bMirroredY );
2929 SdrCustomShapeGeometryItem rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
2930 beans::PropertyValue aPropVal;
2931 aPropVal.Name = "AdjustmentValues";
2932 aPropVal.Value <<= rAGeo.aAdjustmentSeq;
2933 rGeometryItem.SetPropertyValue( aPropVal );
2934 SetMergedItem( rGeometryItem );
2936 InvalidateRenderGeometry();
2939 void SdrObjCustomShape::AdjustToMaxRect(const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */)
2941 SAL_INFO_IF(bShrinkOnly, "svx", "Case bShrinkOnly == true is not implemented yet.");
2943 if (rMaxRect.IsEmpty() || rMaxRect == GetSnapRect())
2944 return;
2946 // Get a matrix, that would produce the existing shape, when applied to a unit square
2947 basegfx::B2DPolyPolygon aPolyPolygon; //not used, but formal needed
2948 basegfx::B2DHomMatrix aMatrix;
2949 TRGetBaseGeometry(aMatrix, aPolyPolygon);
2950 // Using TRSetBaseGeometry(aMatrix, aPolyPolygon) would regenerate the current shape. But
2951 // applying aMatrix to a unit square will not generate the current shape. Scaling,
2952 // rotation and translation are correct, but shear angle has wrong sign. So break up
2953 // matrix and create a mathematically correct new one.
2954 basegfx::B2DTuple aScale;
2955 basegfx::B2DTuple aTranslate;
2956 double fRotate, fShearX;
2957 aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
2958 basegfx::B2DHomMatrix aMathMatrix;
2959 aMathMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
2960 aScale,
2961 basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
2962 basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
2963 aTranslate);
2965 // Calculate scaling factors from size of the transformed unit polygon as ersatz for the not
2966 // usable current snap rectangle.
2967 basegfx::B2DPolygon aB2DPolygon(basegfx::utils::createUnitPolygon());
2968 aB2DPolygon.transform(aMathMatrix);
2969 basegfx::B2DRange aB2DRange(aB2DPolygon.getB2DRange());
2970 double fPolygonWidth = aB2DRange.getWidth();
2971 if (fPolygonWidth == 0)
2972 fPolygonWidth = 1;
2973 double fPolygonHeight = aB2DRange.getHeight();
2974 if (fPolygonHeight == 0)
2975 fPolygonHeight = 1;
2976 const double aFactorX = static_cast<double>(rMaxRect.GetWidth()) / fPolygonWidth;
2977 const double aFactorY = static_cast<double>(rMaxRect.GetHeight()) / fPolygonHeight;
2979 // Generate matrix, that would produce the desired rMaxRect when applied to unit square
2980 aMathMatrix.scale(aFactorX, aFactorY);
2981 aB2DPolygon = basegfx::utils::createUnitPolygon();
2982 aB2DPolygon.transform(aMathMatrix);
2983 aB2DRange = aB2DPolygon.getB2DRange();
2984 const double fPolygonLeft = aB2DRange.getMinX();
2985 const double fPolygonTop = aB2DRange.getMinY();
2986 aMathMatrix.translate(rMaxRect.Left() - fPolygonLeft, rMaxRect.Top() - fPolygonTop);
2988 // Create a Matrix from aMathMatrix, which is usable with TRSetBaseGeometry
2989 aMathMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
2990 aMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
2991 aScale,
2992 basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
2993 basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
2994 aTranslate);
2996 // Now use TRSetBaseGeometry to actually perform scale, shear, rotate and translate
2997 // on the shape. That considers gluepoints, interaction handles and text area, and includes
2998 // setting rectangles dirty and broadcast.
2999 TRSetBaseGeometry(aMatrix, aPolyPolygon);
3002 void SdrObjCustomShape::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
3004 // The shape might have already flipping in its enhanced geometry. LibreOffice applies
3005 // such after all transformations. We remove it, but remember it to apply them later.
3006 bool bIsMirroredX = IsMirroredX();
3007 bool bIsMirroredY = IsMirroredY();
3008 if (bIsMirroredX || bIsMirroredY)
3010 Point aCurrentCenter = GetSnapRect().Center();
3011 if (bIsMirroredX) // mirror on the y-axis
3013 Mirror(aCurrentCenter, Point(aCurrentCenter.X(), aCurrentCenter.Y() + 1000));
3015 if (bIsMirroredY) // mirror on the x-axis
3017 Mirror(aCurrentCenter, Point(aCurrentCenter.X() + 1000, aCurrentCenter.Y()));
3021 // break up matrix
3022 basegfx::B2DTuple aScale;
3023 basegfx::B2DTuple aTranslate;
3024 double fRotate, fShearX;
3025 rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
3027 // reset object shear and rotations
3028 fObjectRotation = 0.0;
3029 maGeo.nRotationAngle = 0_deg100;
3030 maGeo.RecalcSinCos();
3031 maGeo.nShearAngle = 0_deg100;
3032 maGeo.RecalcTan();
3034 // if anchor is used, make position relative to it
3035 if(getSdrModelFromSdrObject().IsWriter())
3037 if(GetAnchorPos().X() || GetAnchorPos().Y())
3039 aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
3043 // scale
3044 Size aSize(FRound(fabs(aScale.getX())), FRound(fabs(aScale.getY())));
3045 // fdo#47434 We need a valid rectangle here
3046 if( !aSize.Height() ) aSize.setHeight( 1 );
3047 if( !aSize.Width() ) aSize.setWidth( 1 );
3048 tools::Rectangle aBaseRect(Point(), aSize);
3049 SetLogicRect(aBaseRect);
3051 // Apply flipping from Matrix, which is a transformation relative to origin
3052 if (basegfx::fTools::less(aScale.getX(), 0.0))
3053 Mirror(Point(0, 0), Point(0, 1000)); // mirror on the y-axis
3054 if (basegfx::fTools::less(aScale.getY(), 0.0))
3055 Mirror(Point(0, 0), Point(1000, 0)); // mirror on the x-axis
3057 // shear?
3058 if(!basegfx::fTools::equalZero(fShearX))
3060 GeoStat aGeoStat;
3061 // #i123181# The fix for #121932# here was wrong, the trunk version does not correct the
3062 // mirrored shear values, neither at the object level, nor on the API or XML level. Taking
3063 // back the mirroring of the shear angle
3064 aGeoStat.nShearAngle = Degree100(FRound(basegfx::rad2deg<100>(atan(fShearX))));
3065 aGeoStat.RecalcTan();
3066 Shear(Point(), aGeoStat.nShearAngle, aGeoStat.mfTanShearAngle, false);
3069 // rotation?
3070 if(!basegfx::fTools::equalZero(fRotate))
3072 GeoStat aGeoStat;
3074 // #i78696#
3075 // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
3076 // mirrored -> mirror value here
3077 aGeoStat.nRotationAngle = NormAngle36000(Degree100(FRound(-basegfx::rad2deg<100>(fRotate))));
3078 aGeoStat.RecalcSinCos();
3079 Rotate(Point(), aGeoStat.nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
3082 // translate?
3083 if(!aTranslate.equalZero())
3085 Move(Size(FRound(aTranslate.getX()), FRound(aTranslate.getY())));
3088 // Apply flipping from enhanced geometry at center of the shape.
3089 if (!(bIsMirroredX || bIsMirroredY))
3090 return;
3092 // create mathematically matrix for the applied transformations
3093 // aScale was in most cases built from a rectangle including edge
3094 // and is therefore mathematically too large by 1
3095 if (aScale.getX() > 2.0 && aScale.getY() > 2.0)
3096 aScale -= basegfx::B2DTuple(1.0, 1.0);
3097 basegfx::B2DHomMatrix aMathMat = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
3098 aScale, -fShearX, basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
3099 aTranslate);
3100 // Use matrix to get current center
3101 basegfx::B2DPoint aCenter(0.5,0.5);
3102 aCenter = aMathMat * aCenter;
3103 double fCenterX = aCenter.getX();
3104 double fCenterY = aCenter.getY();
3105 if (bIsMirroredX) // vertical axis
3106 Mirror(Point(FRound(fCenterX),FRound(fCenterY)),
3107 Point(FRound(fCenterX), FRound(fCenterY + 1000.0)));
3108 if (bIsMirroredY) // horizontal axis
3109 Mirror(Point(FRound(fCenterX),FRound(fCenterY)),
3110 Point(FRound(fCenterX + 1000.0), FRound(fCenterY)));
3113 // taking fObjectRotation instead of aGeo.nAngle
3114 bool SdrObjCustomShape::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
3116 // get turn and shear
3117 double fRotate = basegfx::deg2rad(fObjectRotation);
3118 double fShearX = toRadians(maGeo.nShearAngle);
3120 // get aRectangle, this is the unrotated snaprect
3121 tools::Rectangle aRectangle(getRectangle());
3123 bool bMirroredX = IsMirroredX();
3124 bool bMirroredY = IsMirroredY();
3125 if ( bMirroredX || bMirroredY )
3126 { // we have to retrieve the unmirrored rect
3128 GeoStat aNewGeo(maGeo);
3130 if ( bMirroredX )
3132 fShearX = -fShearX;
3133 tools::Polygon aPol = Rect2Poly(getRectangle(), aNewGeo);
3134 tools::Rectangle aBoundRect( aPol.GetBoundRect() );
3136 Point aRef1( ( aBoundRect.Left() + aBoundRect.Right() ) >> 1, aBoundRect.Top() );
3137 Point aRef2( aRef1.X(), aRef1.Y() + 1000 );
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];
3147 aPol[1]=aPol0[0];
3148 aPol[2]=aPol0[3];
3149 aPol[3]=aPol0[2];
3150 aPol[4]=aPol0[1];
3151 aRectangle = svx::polygonToRectangle(aPol, aNewGeo);
3153 if ( bMirroredY )
3155 fShearX = -fShearX;
3156 tools::Polygon aPol( Rect2Poly( aRectangle, aNewGeo ) );
3157 tools::Rectangle aBoundRect( aPol.GetBoundRect() );
3159 Point aRef1( aBoundRect.Left(), ( aBoundRect.Top() + aBoundRect.Bottom() ) >> 1 );
3160 Point aRef2( aRef1.X() + 1000, aRef1.Y() );
3161 sal_uInt16 i;
3162 sal_uInt16 nPointCount=aPol.GetSize();
3163 for (i=0; i<nPointCount; i++)
3165 MirrorPoint(aPol[i],aRef1,aRef2);
3167 // mirror polygon and move it a bit
3168 tools::Polygon aPol0(aPol);
3169 aPol[0]=aPol0[1]; // This was WRONG for vertical (!)
3170 aPol[1]=aPol0[0]; // #i121932# Despite my own comment above
3171 aPol[2]=aPol0[3]; // it was *not* wrong even when the reordering
3172 aPol[3]=aPol0[2]; // *seems* to be specific for X-Mirrorings. Oh
3173 aPol[4]=aPol0[1]; // will I be happy when this old stuff is |gone| with aw080 (!)
3174 aRectangle = svx::polygonToRectangle(aPol, aNewGeo);
3178 // fill other values
3179 basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
3180 basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
3182 // position may be relative to anchorpos, convert
3183 if(getSdrModelFromSdrObject().IsWriter())
3185 if(GetAnchorPos().X() || GetAnchorPos().Y())
3187 aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
3191 // build matrix
3192 rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
3193 aScale,
3194 basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
3195 basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
3196 aTranslate);
3198 return false;
3201 std::unique_ptr<sdr::contact::ViewContact> SdrObjCustomShape::CreateObjectSpecificViewContact()
3203 return std::make_unique<sdr::contact::ViewContactOfSdrObjCustomShape>(*this);
3206 // #i33136#
3207 bool SdrObjCustomShape::doConstructOrthogonal(std::u16string_view rName)
3209 bool bRetval(false);
3211 if(o3tl::equalsIgnoreAsciiCase(rName, u"quadrat"))
3213 bRetval = true;
3215 else if(o3tl::equalsIgnoreAsciiCase(rName, u"round-quadrat"))
3217 bRetval = true;
3219 else if(o3tl::equalsIgnoreAsciiCase(rName, u"circle"))
3221 bRetval = true;
3223 else if(o3tl::equalsIgnoreAsciiCase(rName, u"circle-pie"))
3225 bRetval = true;
3227 else if(o3tl::equalsIgnoreAsciiCase(rName, u"ring"))
3229 bRetval = true;
3232 return bRetval;
3235 // #i37011# centralize throw-away of render geometry
3236 void SdrObjCustomShape::InvalidateRenderGeometry()
3238 mXRenderedCustomShape = nullptr;
3239 mpLastShadowGeometry = nullptr;
3242 void SdrObjCustomShape::setUnoShape(const uno::Reference<drawing::XShape>& rxUnoShape)
3244 SdrTextObj::setUnoShape(rxUnoShape);
3246 // The shape engine is created with _current_ shape. This means we
3247 // _must_ reset it when the shape changes.
3248 mxCustomShapeEngine.set(nullptr);
3251 OUString SdrObjCustomShape::GetCustomShapeName() const
3253 OUString sShapeName;
3254 OUString aEngine( GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
3255 if ( aEngine.isEmpty()
3256 || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
3258 OUString sShapeType;
3259 const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
3260 const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
3261 if ( pAny && ( *pAny >>= sShapeType ) )
3262 sShapeName = EnhancedCustomShapeTypeNames::GetAccName( sShapeType );
3264 return sShapeName;
3267 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */