1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <basegfx/matrix/b2dhommatrix.hxx>
21 #include <basegfx/matrix/b2dhommatrixtools.hxx>
22 #include <basegfx/point/b2dpoint.hxx>
23 #include <basegfx/polygon/b2dpolygon.hxx>
24 #include <basegfx/polygon/b2dpolygontools.hxx>
26 #include <rtl/ustrbuf.hxx>
28 #include <svx/dialmgr.hxx>
29 #include <svx/strings.hrc>
31 #include <sdr/contact/viewcontactofsdrcircobj.hxx>
32 #include <sdr/properties/circleproperties.hxx>
33 #include <svx/svddrag.hxx>
34 #include <svx/svdmodel.hxx>
35 #include <svx/svdocirc.hxx>
36 #include <svx/svdopath.hxx>
37 #include <svx/svdtrans.hxx>
38 #include <svx/svdview.hxx>
39 #include <svx/sxciaitm.hxx>
40 #include <sxcikitm.hxx>
41 #include <svx/xfillit0.hxx>
42 #include <svx/xlineit0.hxx>
43 #include <svx/xlnedit.hxx>
44 #include <svx/xlnedwit.hxx>
45 #include <svx/xlnstit.hxx>
46 #include <svx/xlnstwit.hxx>
47 #include <svx/xlnwtit.hxx>
48 #include <vcl/canvastools.hxx>
49 #include <vcl/ptrstyle.hxx>
51 using namespace com::sun::star
;
53 static Point
GetAnglePnt(const tools::Rectangle
& rR
, Degree100 nAngle
)
55 Point
aCenter(rR
.Center());
56 tools::Long nWdt
=rR
.Right()-rR
.Left();
57 tools::Long nHgt
=rR
.Bottom()-rR
.Top();
58 tools::Long nMaxRad
=(std::max(nWdt
,nHgt
)+1) /2;
59 double a
= toRadians(nAngle
);
60 Point
aRetval(basegfx::fround
<tools::Long
>(cos(a
) * nMaxRad
),
61 basegfx::fround
<tools::Long
>(-sin(a
) * nMaxRad
));
62 if (nWdt
==0) aRetval
.setX(0 );
63 if (nHgt
==0) aRetval
.setY(0 );
67 // stop possible overruns for very large objects
68 if (std::abs(nHgt
)>32767 || std::abs(aRetval
.Y())>32767) {
69 aRetval
.setY(BigMulDiv(aRetval
.Y(),nHgt
,nWdt
) );
71 aRetval
.setY(aRetval
.Y()*nHgt
/nWdt
);
76 // stop possible overruns for very large objects
77 if (std::abs(nWdt
)>32767 || std::abs(aRetval
.X())>32767) {
78 aRetval
.setX(BigMulDiv(aRetval
.X(),nWdt
,nHgt
) );
80 aRetval
.setX(aRetval
.X()*nWdt
/nHgt
);
90 // BaseProperties section
92 std::unique_ptr
<sdr::properties::BaseProperties
> SdrCircObj::CreateObjectSpecificProperties()
94 return std::make_unique
<sdr::properties::CircleProperties
>(*this);
98 // DrawContact section
100 std::unique_ptr
<sdr::contact::ViewContact
> SdrCircObj::CreateObjectSpecificViewContact()
102 return std::make_unique
<sdr::contact::ViewContactOfSdrCircObj
>(*this);
105 SdrCircKind
ToSdrCircKind(SdrObjKind eKind
)
109 case SdrObjKind::CircleOrEllipse
: return SdrCircKind::Full
;
110 case SdrObjKind::CircleSection
: return SdrCircKind::Section
;
111 case SdrObjKind::CircleArc
: return SdrCircKind::Arc
;
112 case SdrObjKind::CircleCut
: return SdrCircKind::Cut
;
113 default: assert(false);
115 return SdrCircKind::Full
;
118 SdrCircObj::SdrCircObj(
120 SdrCircKind eNewKind
)
121 : SdrRectObj(rSdrModel
)
123 nStartAngle
=0_deg100
;
124 nEndAngle
=36000_deg100
;
125 meCircleKind
=eNewKind
;
126 m_bClosedObj
=eNewKind
!=SdrCircKind::Arc
;
129 SdrCircObj::SdrCircObj(SdrModel
& rSdrModel
, SdrCircObj
const & rSource
)
130 : SdrRectObj(rSdrModel
, rSource
)
132 meCircleKind
= rSource
.meCircleKind
;
133 nStartAngle
= rSource
.nStartAngle
;
134 nEndAngle
= rSource
.nEndAngle
;
135 m_bClosedObj
= rSource
.m_bClosedObj
;
138 SdrCircObj::SdrCircObj(
140 SdrCircKind eNewKind
,
141 const tools::Rectangle
& rRect
)
142 : SdrRectObj(rSdrModel
, rRect
)
144 nStartAngle
=0_deg100
;
145 nEndAngle
=36000_deg100
;
146 meCircleKind
=eNewKind
;
147 m_bClosedObj
=eNewKind
!=SdrCircKind::Arc
;
150 SdrCircObj::SdrCircObj(
152 SdrCircKind eNewKind
,
153 const tools::Rectangle
& rRect
,
154 Degree100 nNewStartAngle
,
155 Degree100 nNewEndAngle
)
156 : SdrRectObj(rSdrModel
, rRect
)
158 Degree100 nAngleDif
=nNewEndAngle
-nNewStartAngle
;
159 nStartAngle
=NormAngle36000(nNewStartAngle
);
160 nEndAngle
=NormAngle36000(nNewEndAngle
);
161 if (nAngleDif
==36000_deg100
) nEndAngle
+=nAngleDif
; // full circle
162 meCircleKind
=eNewKind
;
163 m_bClosedObj
=eNewKind
!=SdrCircKind::Arc
;
166 SdrCircObj::~SdrCircObj()
170 void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec
& rInfo
) const
172 bool bCanConv
=!HasText() || ImpCanConvTextToCurve();
173 rInfo
.bEdgeRadiusAllowed
= false;
174 rInfo
.bCanConvToPath
=bCanConv
;
175 rInfo
.bCanConvToPoly
=bCanConv
;
176 rInfo
.bCanConvToContour
= !IsFontwork() && (rInfo
.bCanConvToPoly
|| LineGeometryUsageIsNecessary());
179 SdrObjKind
SdrCircObj::GetObjIdentifier() const
181 switch (meCircleKind
)
183 case SdrCircKind::Full
: return SdrObjKind::CircleOrEllipse
;
184 case SdrCircKind::Section
: return SdrObjKind::CircleSection
;
185 case SdrCircKind::Cut
: return SdrObjKind::CircleCut
;
186 case SdrCircKind::Arc
: return SdrObjKind::CircleArc
;
187 default: assert(false);
189 return SdrObjKind::CircleOrEllipse
;
192 bool SdrCircObj::PaintNeedsXPolyCirc() const
194 // XPoly is necessary for all rotated ellipse objects, circle and
196 // If not WIN, then (for now) also for circle/ellipse segments and circle/
197 // ellipse arcs (for precision)
198 bool bNeed
= maGeo
.m_nRotationAngle
|| maGeo
.m_nShearAngle
|| meCircleKind
== SdrCircKind::Cut
;
199 // If not WIN, then for everything except full circle (for now!)
200 if (meCircleKind
!=SdrCircKind::Full
) bNeed
= true;
202 const SfxItemSet
& rSet
= GetObjectItemSet();
205 // XPoly is necessary for everything that isn't LineSolid or LineNone
206 drawing::LineStyle eLine
= rSet
.Get(XATTR_LINESTYLE
).GetValue();
207 bNeed
= eLine
!= drawing::LineStyle_NONE
&& eLine
!= drawing::LineStyle_SOLID
;
209 // XPoly is necessary for thick lines
210 if(!bNeed
&& eLine
!= drawing::LineStyle_NONE
)
211 bNeed
= rSet
.Get(XATTR_LINEWIDTH
).GetValue() != 0;
213 // XPoly is necessary for circle arcs with line ends
214 if(!bNeed
&& meCircleKind
== SdrCircKind::Arc
)
216 // start of the line is here if StartPolygon, StartWidth!=0
217 bNeed
=rSet
.Get(XATTR_LINESTART
).GetLineStartValue().count() != 0 &&
218 rSet
.Get(XATTR_LINESTARTWIDTH
).GetValue() != 0;
222 // end of the line is here if EndPolygon, EndWidth!=0
223 bNeed
= rSet
.Get(XATTR_LINEEND
).GetLineEndValue().count() != 0 &&
224 rSet
.Get(XATTR_LINEENDWIDTH
).GetValue() != 0;
229 // XPoly is necessary if Fill !=None and !=Solid
230 if(!bNeed
&& meCircleKind
!= SdrCircKind::Arc
)
232 drawing::FillStyle eFill
=rSet
.Get(XATTR_FILLSTYLE
).GetValue();
233 bNeed
= eFill
!= drawing::FillStyle_NONE
&& eFill
!= drawing::FillStyle_SOLID
;
236 if(!bNeed
&& meCircleKind
!= SdrCircKind::Full
&& nStartAngle
== nEndAngle
)
237 bNeed
= true; // otherwise we're drawing a full circle
242 basegfx::B2DPolygon
SdrCircObj::ImpCalcXPolyCirc(const SdrCircKind eCircleKind
, const tools::Rectangle
& rRect1
, Degree100 nStart
, Degree100 nEnd
) const
244 const basegfx::B2DRange aRange
= vcl::unotools::b2DRectangleFromRectangle(rRect1
);
245 basegfx::B2DPolygon aCircPolygon
;
247 if(SdrCircKind::Full
== eCircleKind
)
249 // create full circle. Do not use createPolygonFromEllipse; it's necessary
250 // to get the start point to the bottom of the circle to keep compatible to
251 // old geometry creation
252 aCircPolygon
= basegfx::utils::createPolygonFromUnitCircle(1);
254 // needs own scaling and translation from unit circle to target size (same as
255 // would be in createPolygonFromEllipse)
256 const basegfx::B2DPoint
aCenter(aRange
.getCenter());
257 const basegfx::B2DHomMatrix
aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
258 aRange
.getWidth() / 2.0, aRange
.getHeight() / 2.0,
259 aCenter
.getX(), aCenter
.getY()));
260 aCircPolygon
.transform(aMatrix
);
264 // mirror start, end for geometry creation since model coordinate system is mirrored in Y
265 const double fStart(toRadians((36000_deg100
- nEnd
) % 36000_deg100
));
266 const double fEnd(toRadians((36000_deg100
- nStart
) % 36000_deg100
));
268 // create circle segment. This is not closed by default
269 aCircPolygon
= basegfx::utils::createPolygonFromEllipseSegment(
270 aRange
.getCenter(), aRange
.getWidth() / 2.0, aRange
.getHeight() / 2.0,
273 // check closing states
274 const bool bCloseSegment(SdrCircKind::Arc
!= eCircleKind
);
275 const bool bCloseUsingCenter(SdrCircKind::Section
== eCircleKind
);
279 if(bCloseUsingCenter
)
281 // add center point at start (for historical reasons)
282 basegfx::B2DPolygon aSector
;
283 aSector
.append(aRange
.getCenter());
284 aSector
.append(aCircPolygon
);
285 aCircPolygon
= std::move(aSector
);
289 aCircPolygon
.setClosed(true);
294 if (maGeo
.m_nShearAngle
|| maGeo
.m_nRotationAngle
)
296 // translate top left to (0,0)
297 const basegfx::B2DPoint
aTopLeft(aRange
.getMinimum());
298 basegfx::B2DHomMatrix
aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
299 -aTopLeft
.getX(), -aTopLeft
.getY()));
301 // shear, rotate and back to top left (if needed)
302 aMatrix
= basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
303 -maGeo
.mfTanShearAngle
,
304 maGeo
.m_nRotationAngle
? toRadians(36000_deg100
- maGeo
.m_nRotationAngle
) : 0.0,
307 // apply transformation
308 aCircPolygon
.transform(aMatrix
);
314 void SdrCircObj::RecalcXPoly()
316 basegfx::B2DPolygon
aPolyCirc(ImpCalcXPolyCirc(meCircleKind
, getRectangle(), nStartAngle
, nEndAngle
));
317 mpXPoly
= XPolygon(aPolyCirc
);
320 OUString
SdrCircObj::TakeObjNameSingul() const
322 TranslateId pID
=STR_ObjNameSingulCIRC
;
323 if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo
.m_nShearAngle
== 0_deg100
)
325 switch (meCircleKind
) {
326 case SdrCircKind::Full
: pID
=STR_ObjNameSingulCIRC
; break;
327 case SdrCircKind::Section
: pID
=STR_ObjNameSingulSECT
; break;
328 case SdrCircKind::Arc
: pID
=STR_ObjNameSingulCARC
; break;
329 case SdrCircKind::Cut
: pID
=STR_ObjNameSingulCCUT
; break;
333 switch (meCircleKind
) {
334 case SdrCircKind::Full
: pID
=STR_ObjNameSingulCIRCE
; break;
335 case SdrCircKind::Section
: pID
=STR_ObjNameSingulSECTE
; break;
336 case SdrCircKind::Arc
: pID
=STR_ObjNameSingulCARCE
; break;
337 case SdrCircKind::Cut
: pID
=STR_ObjNameSingulCCUTE
; break;
341 OUString
sName(SvxResId(pID
));
343 OUString
aName(GetName());
344 if (!aName
.isEmpty())
345 sName
+= " '" + aName
+ "'";
349 OUString
SdrCircObj::TakeObjNamePlural() const
351 TranslateId pID
=STR_ObjNamePluralCIRC
;
352 if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo
.m_nShearAngle
== 0_deg100
)
354 switch (meCircleKind
) {
355 case SdrCircKind::Full
: pID
=STR_ObjNamePluralCIRC
; break;
356 case SdrCircKind::Section
: pID
=STR_ObjNamePluralSECT
; break;
357 case SdrCircKind::Arc
: pID
=STR_ObjNamePluralCARC
; break;
358 case SdrCircKind::Cut
: pID
=STR_ObjNamePluralCCUT
; break;
362 switch (meCircleKind
) {
363 case SdrCircKind::Full
: pID
=STR_ObjNamePluralCIRCE
; break;
364 case SdrCircKind::Section
: pID
=STR_ObjNamePluralSECTE
; break;
365 case SdrCircKind::Arc
: pID
=STR_ObjNamePluralCARCE
; break;
366 case SdrCircKind::Cut
: pID
=STR_ObjNamePluralCCUTE
; break;
370 return SvxResId(pID
);
373 rtl::Reference
<SdrObject
> SdrCircObj::CloneSdrObject(SdrModel
& rTargetModel
) const
375 return new SdrCircObj(rTargetModel
, *this);
378 basegfx::B2DPolyPolygon
SdrCircObj::TakeXorPoly() const
380 const basegfx::B2DPolygon
aCircPolygon(ImpCalcXPolyCirc(meCircleKind
, getRectangle(), nStartAngle
, nEndAngle
));
381 return basegfx::B2DPolyPolygon(aCircPolygon
);
386 struct ImpCircUser
: public SdrDragStatUserData
403 void SetCreateParams(SdrDragStat
const & rStat
);
408 sal_uInt32
SdrCircObj::GetHdlCount() const
410 if(SdrCircKind::Full
!= meCircleKind
)
420 void SdrCircObj::AddToHdlList(SdrHdlList
& rHdlList
) const
422 for (sal_uInt32 nHdlNum
=(SdrCircKind::Full
==meCircleKind
)?2:0; nHdlNum
<=9; ++nHdlNum
)
425 SdrHdlKind
eLocalKind(SdrHdlKind::Move
);
427 tools::Rectangle aRectangle
= getRectangle();
431 aPnt
= GetAnglePnt(aRectangle
, nStartAngle
);
432 eLocalKind
= SdrHdlKind::Circle
;
436 aPnt
= GetAnglePnt(aRectangle
, nEndAngle
);
437 eLocalKind
= SdrHdlKind::Circle
;
441 aPnt
= aRectangle
.TopLeft();
442 eLocalKind
= SdrHdlKind::UpperLeft
;
445 aPnt
= aRectangle
.TopCenter();
446 eLocalKind
= SdrHdlKind::Upper
;
449 aPnt
= aRectangle
.TopRight();
450 eLocalKind
= SdrHdlKind::UpperRight
;
453 aPnt
= aRectangle
.LeftCenter();
454 eLocalKind
= SdrHdlKind::Left
;
457 aPnt
= aRectangle
.RightCenter();
458 eLocalKind
= SdrHdlKind::Right
;
461 aPnt
= aRectangle
.BottomLeft();
462 eLocalKind
= SdrHdlKind::LowerLeft
;
465 aPnt
= aRectangle
.BottomCenter();
466 eLocalKind
= SdrHdlKind::Lower
;
469 aPnt
= aRectangle
.BottomRight();
470 eLocalKind
= SdrHdlKind::LowerRight
;
474 if (maGeo
.m_nShearAngle
)
476 ShearPoint(aPnt
, aRectangle
.TopLeft(), maGeo
.mfTanShearAngle
);
479 if (maGeo
.m_nRotationAngle
)
481 RotatePoint(aPnt
, aRectangle
.TopLeft(), maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
484 std::unique_ptr
<SdrHdl
> pH(new SdrHdl(aPnt
,eLocalKind
));
485 pH
->SetPointNum(nPNum
);
486 pH
->SetObj(const_cast<SdrCircObj
*>(this));
487 pH
->SetRotationAngle(maGeo
.m_nRotationAngle
);
488 rHdlList
.AddHdl(std::move(pH
));
493 bool SdrCircObj::hasSpecialDrag() const
498 bool SdrCircObj::beginSpecialDrag(SdrDragStat
& rDrag
) const
500 const bool bAngle(rDrag
.GetHdl() && SdrHdlKind::Circle
== rDrag
.GetHdl()->GetKind());
504 if(1 == rDrag
.GetHdl()->GetPointNum() || 2 == rDrag
.GetHdl()->GetPointNum())
512 return SdrTextObj::beginSpecialDrag(rDrag
);
515 bool SdrCircObj::applySpecialDrag(SdrDragStat
& rDrag
)
517 const bool bAngle(rDrag
.GetHdl() && SdrHdlKind::Circle
== rDrag
.GetHdl()->GetKind());
521 Point
aPt(rDrag
.GetNow());
523 if (maGeo
.m_nRotationAngle
)
524 RotatePoint(aPt
, getRectangle().TopLeft(), -maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
526 if (maGeo
.m_nShearAngle
)
527 ShearPoint(aPt
, getRectangle().TopLeft(), -maGeo
.mfTanShearAngle
);
529 aPt
-= getRectangle().Center();
531 tools::Long nWdt
= getRectangle().Right() - getRectangle().Left();
532 tools::Long nHgt
= getRectangle().Bottom() - getRectangle().Top();
536 aPt
.setY(BigMulDiv(aPt
.Y(),nWdt
,nHgt
) );
540 aPt
.setX(BigMulDiv(aPt
.X(),nHgt
,nWdt
) );
543 Degree100 nAngle
=NormAngle36000(GetAngle(aPt
));
545 if (rDrag
.GetView() && rDrag
.GetView()->IsAngleSnapEnabled())
547 Degree100 nSA
=rDrag
.GetView()->GetSnapAngle();
551 nAngle
+=nSA
/2_deg100
;
554 nAngle
=NormAngle36000(nAngle
);
558 if(1 == rDrag
.GetHdl()->GetPointNum())
560 nStartAngle
= nAngle
;
562 else if(2 == rDrag
.GetHdl()->GetPointNum())
567 SetBoundAndSnapRectsDirty();
569 ImpSetCircInfoToAttr();
576 return SdrTextObj::applySpecialDrag(rDrag
);
580 OUString
SdrCircObj::getSpecialDragComment(const SdrDragStat
& rDrag
) const
582 const bool bCreateComment(rDrag
.GetView() && this == rDrag
.GetView()->GetCreateObj());
586 OUStringBuffer
aBuf(ImpGetDescriptionStr(STR_ViewCreateObj
));
587 const sal_uInt32
nPointCount(rDrag
.GetPointCount());
589 if(SdrCircKind::Full
!= meCircleKind
&& nPointCount
> 2)
591 const ImpCircUser
* pU
= static_cast<const ImpCircUser
*>(rDrag
.GetUser());
605 aBuf
.append(SdrModel::GetAngleString(nAngle
));
609 return aBuf
.makeStringAndClear();
613 const bool bAngle(rDrag
.GetHdl() && SdrHdlKind::Circle
== rDrag
.GetHdl()->GetKind());
617 const Degree100
nAngle(1 == rDrag
.GetHdl()->GetPointNum() ? nStartAngle
: nEndAngle
);
619 return ImpGetDescriptionStr(STR_DragCircAngle
) +
621 SdrModel::GetAngleString(nAngle
) +
626 return SdrTextObj::getSpecialDragComment(rDrag
);
632 void ImpCircUser::SetCreateParams(SdrDragStat
const & rStat
)
634 rStat
.TakeCreateRect(aR
);
637 nWdt
=aR
.Right()-aR
.Left();
638 nHgt
=aR
.Bottom()-aR
.Top();
641 if (rStat
.GetPointCount()>2) {
642 Point
aP(rStat
.GetPoint(2)-aCenter
);
643 if (nWdt
==0) aP
.setX(0 );
644 if (nHgt
==0) aP
.setY(0 );
646 if (nHgt
!=0) aP
.setY(aP
.Y()*nWdt
/nHgt
);
648 if (nWdt
!=0) aP
.setX(aP
.X()*nHgt
/nWdt
);
650 nStart
=NormAngle36000(GetAngle(aP
));
651 if (rStat
.GetView()!=nullptr && rStat
.GetView()->IsAngleSnapEnabled()) {
652 Degree100 nSA
=rStat
.GetView()->GetSnapAngle();
653 if (nSA
) { // angle snapping
654 nStart
+=nSA
/2_deg100
;
657 nStart
=NormAngle36000(nStart
);
660 aP1
= GetAnglePnt(aR
,nStart
);
663 if (rStat
.GetPointCount()<=3)
666 Point
aP(rStat
.GetPoint(3)-aCenter
);
668 aP
.setY(BigMulDiv(aP
.Y(),nWdt
,nHgt
) );
670 aP
.setX(BigMulDiv(aP
.X(),nHgt
,nWdt
) );
672 nEnd
=NormAngle36000(GetAngle(aP
));
673 if (rStat
.GetView()!=nullptr && rStat
.GetView()->IsAngleSnapEnabled()) {
674 Degree100 nSA
=rStat
.GetView()->GetSnapAngle();
675 if (nSA
) { // angle snapping
679 nEnd
=NormAngle36000(nEnd
);
684 void SdrCircObj::ImpSetCreateParams(SdrDragStat
& rStat
)
686 ImpCircUser
* pU
=static_cast<ImpCircUser
*>(rStat
.GetUser());
689 rStat
.SetUser(std::unique_ptr
<ImpCircUser
>(pU
));
691 pU
->SetCreateParams(rStat
);
694 bool SdrCircObj::BegCreate(SdrDragStat
& rStat
)
696 rStat
.SetOrtho4Possible();
697 tools::Rectangle
aRect1(rStat
.GetStart(), rStat
.GetNow());
699 rStat
.SetActionRect(aRect1
);
700 setRectangle(aRect1
);
701 ImpSetCreateParams(rStat
);
705 bool SdrCircObj::MovCreate(SdrDragStat
& rStat
)
707 ImpSetCreateParams(rStat
);
708 ImpCircUser
* pU
=static_cast<ImpCircUser
*>(rStat
.GetUser());
709 rStat
.SetActionRect(pU
->aR
);
710 setRectangle(pU
->aR
); // for ObjName
711 ImpJustifyRect(maRectangle
);
712 nStartAngle
=pU
->nStart
;
715 m_bSnapRectDirty
=true;
718 // #i103058# push current angle settings to ItemSet to
719 // allow FullDrag visualisation
720 if(rStat
.GetPointCount() >= 4)
722 ImpSetCircInfoToAttr();
728 bool SdrCircObj::EndCreate(SdrDragStat
& rStat
, SdrCreateCmd eCmd
)
730 ImpSetCreateParams(rStat
);
731 ImpCircUser
* pU
=static_cast<ImpCircUser
*>(rStat
.GetUser());
733 if (eCmd
==SdrCreateCmd::ForceEnd
&& rStat
.GetPointCount()<4) meCircleKind
=SdrCircKind::Full
;
734 if (meCircleKind
==SdrCircKind::Full
) {
735 bRet
=rStat
.GetPointCount()>=2;
737 tools::Rectangle
aRectangle(pU
->aR
);
738 ImpJustifyRect(aRectangle
);
739 setRectangle(aRectangle
);
742 rStat
.SetNoSnap(rStat
.GetPointCount()>=2);
743 rStat
.SetOrtho4Possible(rStat
.GetPointCount()<2);
744 bRet
=rStat
.GetPointCount()>=4;
746 tools::Rectangle
aRectangle(pU
->aR
);
747 ImpJustifyRect(aRectangle
);
748 setRectangle(aRectangle
);
749 nStartAngle
=pU
->nStart
;
753 m_bClosedObj
=meCircleKind
!=SdrCircKind::Arc
;
754 SetBoundAndSnapRectsDirty();
756 ImpSetCircInfoToAttr();
758 rStat
.SetUser(nullptr);
762 void SdrCircObj::BrkCreate(SdrDragStat
& rStat
)
764 rStat
.SetUser(nullptr);
767 bool SdrCircObj::BckCreate(SdrDragStat
& rStat
)
769 rStat
.SetNoSnap(rStat
.GetPointCount()>=3);
770 rStat
.SetOrtho4Possible(rStat
.GetPointCount()<3);
771 return meCircleKind
!=SdrCircKind::Full
;
774 basegfx::B2DPolyPolygon
SdrCircObj::TakeCreatePoly(const SdrDragStat
& rDrag
) const
776 const ImpCircUser
* pU
= static_cast<const ImpCircUser
*>(rDrag
.GetUser());
778 if(rDrag
.GetPointCount() < 4)
780 // force to OBJ_CIRC to get full visualisation
781 basegfx::B2DPolyPolygon
aRetval(ImpCalcXPolyCirc(SdrCircKind::Full
, pU
->aR
, pU
->nStart
, pU
->nEnd
));
783 if(3 == rDrag
.GetPointCount())
785 // add edge to first point on ellipse
786 basegfx::B2DPolygon aNew
;
788 aNew
.append(basegfx::B2DPoint(pU
->aCenter
.X(), pU
->aCenter
.Y()));
789 aNew
.append(basegfx::B2DPoint(pU
->aP1
.X(), pU
->aP1
.Y()));
790 aRetval
.append(aNew
);
797 return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind
, pU
->aR
, pU
->nStart
, pU
->nEnd
));
801 PointerStyle
SdrCircObj::GetCreatePointer() const
803 switch (meCircleKind
) {
804 case SdrCircKind::Full
: return PointerStyle::DrawEllipse
;
805 case SdrCircKind::Section
: return PointerStyle::DrawPie
;
806 case SdrCircKind::Arc
: return PointerStyle::DrawArc
;
807 case SdrCircKind::Cut
: return PointerStyle::DrawCircleCut
;
810 return PointerStyle::Cross
;
813 void SdrCircObj::NbcMove(const Size
& aSize
)
815 moveRectangle(aSize
.Width(), aSize
.Height());
816 moveOutRectangle(aSize
.Width(), aSize
.Height());
817 maSnapRect
.Move(aSize
);
819 SetBoundAndSnapRectsDirty(true);
822 void SdrCircObj::NbcResize(const Point
& rRef
, const Fraction
& xFact
, const Fraction
& yFact
)
824 Degree100 nAngle0
= maGeo
.m_nRotationAngle
;
825 bool bNoShearRota
= (maGeo
.m_nRotationAngle
== 0_deg100
&& maGeo
.m_nShearAngle
== 0_deg100
);
826 SdrTextObj::NbcResize(rRef
,xFact
,yFact
);
827 bNoShearRota
|= (maGeo
.m_nRotationAngle
== 0_deg100
&& maGeo
.m_nShearAngle
== 0_deg100
);
828 if (meCircleKind
!=SdrCircKind::Full
) {
829 bool bXMirr
=(xFact
.GetNumerator()<0) != (xFact
.GetDenominator()<0);
830 bool bYMirr
=(yFact
.GetNumerator()<0) != (yFact
.GetDenominator()<0);
831 if (bXMirr
|| bYMirr
) {
832 // At bXMirr!=bYMirr we should actually swap both line ends.
833 // That, however, is pretty bad (because of forced "hard" formatting).
834 // Alternatively, we could implement a bMirrored flag (maybe even
835 // a more general one, e. g. for mirrored text, ...).
836 Degree100 nS0
=nStartAngle
;
837 Degree100 nE0
=nEndAngle
;
839 // the RectObj already mirrors at VMirror because of a 180deg rotation
840 if (! (bXMirr
&& bYMirr
)) {
842 nS0
=18000_deg100
-nE0
;
843 nE0
=18000_deg100
-nTmp
;
845 } else { // mirror contorted ellipses
846 if (bXMirr
!=bYMirr
) {
851 nS0
=18000_deg100
-nE0
;
852 nE0
=18000_deg100
-nTmp
;
859 nS0
-= maGeo
.m_nRotationAngle
;
860 nE0
-= maGeo
.m_nRotationAngle
;
863 Degree100 nAngleDif
=nE0
-nS0
;
864 nStartAngle
=NormAngle36000(nS0
);
865 nEndAngle
=NormAngle36000(nE0
);
866 if (nAngleDif
==36000_deg100
) nEndAngle
+=nAngleDif
; // full circle
870 ImpSetCircInfoToAttr();
873 void SdrCircObj::NbcShear(const Point
& rRef
, Degree100 nAngle
, double tn
, bool bVShear
)
875 SdrTextObj::NbcShear(rRef
,nAngle
,tn
,bVShear
);
877 ImpSetCircInfoToAttr();
880 void SdrCircObj::NbcMirror(const Point
& rRef1
, const Point
& rRef2
)
882 bool bFreeMirr
=meCircleKind
!=SdrCircKind::Full
;
885 if (bFreeMirr
) { // some preparations for using an arbitrary axis of reflection
886 Point
aCenter(getRectangle().Center());
887 tools::Long nWdt
= getRectangle().GetWidth() - 1;
888 tools::Long nHgt
= getRectangle().GetHeight() - 1;
889 tools::Long nMaxRad
=(std::max(nWdt
,nHgt
)+1) /2;
891 double a
= toRadians(nStartAngle
);
892 aTmpPt1
= Point(basegfx::fround
<tools::Long
>(cos(a
) * nMaxRad
),
893 basegfx::fround
<tools::Long
>(-sin(a
) * nMaxRad
));
894 if (nWdt
==0) aTmpPt1
.setX(0 );
895 if (nHgt
==0) aTmpPt1
.setY(0 );
898 a
= toRadians(nEndAngle
);
899 aTmpPt2
= Point(basegfx::fround
<tools::Long
>(cos(a
) * nMaxRad
),
900 basegfx::fround
<tools::Long
>(-sin(a
) * nMaxRad
));
901 if (nWdt
==0) aTmpPt2
.setX(0 );
902 if (nHgt
==0) aTmpPt2
.setY(0 );
904 if (maGeo
.m_nRotationAngle
)
906 RotatePoint(aTmpPt1
, getRectangle().TopLeft(), maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
907 RotatePoint(aTmpPt2
, getRectangle().TopLeft(), maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
909 if (maGeo
.m_nShearAngle
)
911 ShearPoint(aTmpPt1
, getRectangle().TopLeft(), maGeo
.mfTanShearAngle
);
912 ShearPoint(aTmpPt2
, getRectangle().TopLeft(), maGeo
.mfTanShearAngle
);
915 SdrTextObj::NbcMirror(rRef1
,rRef2
);
916 if (meCircleKind
!=SdrCircKind::Full
) { // adapt starting and finishing angle
917 MirrorPoint(aTmpPt1
,rRef1
,rRef2
);
918 MirrorPoint(aTmpPt2
,rRef1
,rRef2
);
920 if (maGeo
.m_nRotationAngle
)
922 RotatePoint(aTmpPt1
, getRectangle().TopLeft(), -maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
); // -sin for reversion
923 RotatePoint(aTmpPt2
, getRectangle().TopLeft(), -maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
); // -sin for reversion
926 if (maGeo
.m_nShearAngle
)
928 ShearPoint(aTmpPt1
, getRectangle().TopLeft(), -maGeo
.mfTanShearAngle
); // -tan for reversion
929 ShearPoint(aTmpPt2
, getRectangle().TopLeft(), -maGeo
.mfTanShearAngle
); // -tan for reversion
931 Point
aCenter(getRectangle().Center());
934 // because it's mirrored, the angles are swapped, too
935 nStartAngle
=GetAngle(aTmpPt2
);
936 nEndAngle
=GetAngle(aTmpPt1
);
937 Degree100 nAngleDif
=nEndAngle
-nStartAngle
;
938 nStartAngle
=NormAngle36000(nStartAngle
);
939 nEndAngle
=NormAngle36000(nEndAngle
);
940 if (nAngleDif
==36000_deg100
) nEndAngle
+=nAngleDif
; // full circle
943 ImpSetCircInfoToAttr();
946 std::unique_ptr
<SdrObjGeoData
> SdrCircObj::NewGeoData() const
948 return std::make_unique
<SdrCircObjGeoData
>();
951 void SdrCircObj::SaveGeoData(SdrObjGeoData
& rGeo
) const
953 SdrRectObj::SaveGeoData(rGeo
);
954 SdrCircObjGeoData
& rCGeo
=static_cast<SdrCircObjGeoData
&>(rGeo
);
955 rCGeo
.nStartAngle
=nStartAngle
;
956 rCGeo
.nEndAngle
=nEndAngle
;
959 void SdrCircObj::RestoreGeoData(const SdrObjGeoData
& rGeo
)
961 SdrRectObj::RestoreGeoData(rGeo
);
962 const SdrCircObjGeoData
& rCGeo
=static_cast<const SdrCircObjGeoData
&>(rGeo
);
963 nStartAngle
=rCGeo
.nStartAngle
;
964 nEndAngle
=rCGeo
.nEndAngle
;
966 ImpSetCircInfoToAttr();
969 static void Union(tools::Rectangle
& rR
, const Point
& rP
)
971 if (rP
.X()<rR
.Left ()) rR
.SetLeft(rP
.X() );
972 if (rP
.X()>rR
.Right ()) rR
.SetRight(rP
.X() );
973 if (rP
.Y()<rR
.Top ()) rR
.SetTop(rP
.Y() );
974 if (rP
.Y()>rR
.Bottom()) rR
.SetBottom(rP
.Y() );
977 void SdrCircObj::TakeUnrotatedSnapRect(tools::Rectangle
& rRect
) const
979 rRect
= getRectangle();
980 if (meCircleKind
!=SdrCircKind::Full
) {
981 const Point
aPntStart(GetAnglePnt(getRectangle(), nStartAngle
));
982 const Point
aPntEnd(GetAnglePnt(getRectangle(), nEndAngle
));
983 Degree100 a
=nStartAngle
;
984 Degree100 e
=nEndAngle
;
985 rRect
.SetLeft(getRectangle().Right() );
986 rRect
.SetRight(getRectangle().Left() );
987 rRect
.SetTop(getRectangle().Bottom() );
988 rRect
.SetBottom(getRectangle().Top() );
989 Union(rRect
,aPntStart
);
990 Union(rRect
,aPntEnd
);
991 if ((a
<=18000_deg100
&& e
>=18000_deg100
) || (a
>e
&& (a
<=18000_deg100
|| e
>=18000_deg100
))) {
992 Union(rRect
, getRectangle().LeftCenter());
994 if ((a
<=27000_deg100
&& e
>=27000_deg100
) || (a
>e
&& (a
<=27000_deg100
|| e
>=27000_deg100
))) {
995 Union(rRect
, getRectangle().BottomCenter());
998 Union(rRect
, getRectangle().RightCenter());
1000 if ((a
<=9000_deg100
&& e
>=9000_deg100
) || (a
>e
&& (a
<=9000_deg100
|| e
>=9000_deg100
))) {
1001 Union(rRect
, getRectangle().TopCenter());
1003 if (meCircleKind
==SdrCircKind::Section
) {
1004 Union(rRect
, getRectangle().Center());
1006 if (maGeo
.m_nRotationAngle
)
1008 Point
aDst(rRect
.TopLeft());
1009 aDst
-= getRectangle().TopLeft();
1011 RotatePoint(aDst
,Point(), maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
1013 rRect
.Move(aDst
.X(),aDst
.Y());
1016 if (maGeo
.m_nShearAngle
==0_deg100
)
1019 tools::Long nDst
= basegfx::fround
<tools::Long
>((rRect
.Bottom() - rRect
.Top()) * maGeo
.mfTanShearAngle
);
1020 if (maGeo
.m_nShearAngle
> 0_deg100
)
1022 Point
aRef(rRect
.TopLeft());
1023 rRect
.AdjustLeft( -nDst
);
1024 Point
aTmpPt(rRect
.TopLeft());
1025 RotatePoint(aTmpPt
, aRef
, maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
1026 aTmpPt
-=rRect
.TopLeft();
1027 rRect
.Move(aTmpPt
.X(),aTmpPt
.Y());
1029 rRect
.AdjustRight( -nDst
);
1033 void SdrCircObj::RecalcSnapRect()
1035 if (PaintNeedsXPolyCirc()) {
1036 maSnapRect
=GetXPoly().GetBoundRect();
1038 TakeUnrotatedSnapRect(maSnapRect
);
1042 void SdrCircObj::NbcSetSnapRect(const tools::Rectangle
& rRect
)
1044 if (maGeo
.m_nRotationAngle
|| maGeo
.m_nShearAngle
|| meCircleKind
!= SdrCircKind::Full
)
1046 tools::Rectangle
aSR0(GetSnapRect());
1047 tools::Long nWdt0
=aSR0
.Right()-aSR0
.Left();
1048 tools::Long nHgt0
=aSR0
.Bottom()-aSR0
.Top();
1049 tools::Long nWdt1
=rRect
.Right()-rRect
.Left();
1050 tools::Long nHgt1
=rRect
.Bottom()-rRect
.Top();
1051 NbcResize(maSnapRect
.TopLeft(),Fraction(nWdt1
,nWdt0
),Fraction(nHgt1
,nHgt0
));
1052 NbcMove(Size(rRect
.Left()-aSR0
.Left(),rRect
.Top()-aSR0
.Top()));
1054 setRectangle(rRect
);
1055 ImpJustifyRect(maRectangle
);
1057 SetBoundAndSnapRectsDirty();
1059 ImpSetCircInfoToAttr();
1062 sal_uInt32
SdrCircObj::GetSnapPointCount() const
1064 if (meCircleKind
==SdrCircKind::Full
) {
1071 Point
SdrCircObj::GetSnapPoint(sal_uInt32 i
) const
1075 case 1 : return GetAnglePnt(getRectangle(), nStartAngle
);
1076 case 2 : return GetAnglePnt(getRectangle(), nEndAngle
);
1077 default: return getRectangle().Center();
1081 void SdrCircObj::Notify(SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
1084 SdrRectObj::Notify(rBC
,rHint
);
1085 ImpSetAttrToCircInfo();
1089 void SdrCircObj::ImpSetAttrToCircInfo()
1091 const SfxItemSet
& rSet
= GetObjectItemSet();
1092 SdrCircKind eNewKind
= rSet
.Get(SDRATTR_CIRCKIND
).GetValue();
1094 Degree100 nNewStart
= rSet
.Get(SDRATTR_CIRCSTARTANGLE
).GetValue();
1095 Degree100 nNewEnd
= rSet
.Get(SDRATTR_CIRCENDANGLE
).GetValue();
1097 bool bKindChg
= meCircleKind
!= eNewKind
;
1098 bool bAngleChg
= nNewStart
!= nStartAngle
|| nNewEnd
!= nEndAngle
;
1100 if(bKindChg
|| bAngleChg
)
1102 meCircleKind
= eNewKind
;
1103 nStartAngle
= nNewStart
;
1104 nEndAngle
= nNewEnd
;
1106 if(bKindChg
|| (meCircleKind
!= SdrCircKind::Full
&& bAngleChg
))
1109 SetBoundAndSnapRectsDirty();
1114 void SdrCircObj::ImpSetCircInfoToAttr()
1116 const SfxItemSet
& rSet
= GetObjectItemSet();
1118 SdrCircKind eOldKindA
= rSet
.Get(SDRATTR_CIRCKIND
).GetValue();
1119 Degree100 nOldStartAngle
= rSet
.Get(SDRATTR_CIRCSTARTANGLE
).GetValue();
1120 Degree100 nOldEndAngle
= rSet
.Get(SDRATTR_CIRCENDANGLE
).GetValue();
1122 if(meCircleKind
== eOldKindA
&& nStartAngle
== nOldStartAngle
&& nEndAngle
== nOldEndAngle
)
1125 // since SetItem() implicitly calls ImpSetAttrToCircInfo()
1126 // setting the item directly is necessary here.
1127 if(meCircleKind
!= eOldKindA
)
1129 GetProperties().SetObjectItemDirect(SdrCircKindItem(meCircleKind
));
1132 if(nStartAngle
!= nOldStartAngle
)
1134 GetProperties().SetObjectItemDirect(makeSdrCircStartAngleItem(nStartAngle
));
1137 if(nEndAngle
!= nOldEndAngle
)
1139 GetProperties().SetObjectItemDirect(makeSdrCircEndAngleItem(nEndAngle
));
1143 ImpSetAttrToCircInfo();
1146 rtl::Reference
<SdrObject
> SdrCircObj::DoConvertToPolyObj(bool bBezier
, bool bAddText
) const
1148 const bool bFill(meCircleKind
!= SdrCircKind::Arc
);
1149 const basegfx::B2DPolygon
aCircPolygon(ImpCalcXPolyCirc(meCircleKind
, getRectangle(), nStartAngle
, nEndAngle
));
1150 rtl::Reference
<SdrObject
> pRet
= ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon
), bFill
, bBezier
);
1154 pRet
= ImpConvertAddText(std::move(pRet
), bBezier
);
1160 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */