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(FRound(cos(a
)*nMaxRad
),-FRound(sin(a
)*nMaxRad
));
61 if (nWdt
==0) aRetval
.setX(0 );
62 if (nHgt
==0) aRetval
.setY(0 );
66 // stop possible overruns for very large objects
67 if (std::abs(nHgt
)>32767 || std::abs(aRetval
.Y())>32767) {
68 aRetval
.setY(BigMulDiv(aRetval
.Y(),nHgt
,nWdt
) );
70 aRetval
.setY(aRetval
.Y()*nHgt
/nWdt
);
75 // stop possible overruns for very large objects
76 if (std::abs(nWdt
)>32767 || std::abs(aRetval
.X())>32767) {
77 aRetval
.setX(BigMulDiv(aRetval
.X(),nWdt
,nHgt
) );
79 aRetval
.setX(aRetval
.X()*nWdt
/nHgt
);
89 // BaseProperties section
91 std::unique_ptr
<sdr::properties::BaseProperties
> SdrCircObj::CreateObjectSpecificProperties()
93 return std::make_unique
<sdr::properties::CircleProperties
>(*this);
97 // DrawContact section
99 std::unique_ptr
<sdr::contact::ViewContact
> SdrCircObj::CreateObjectSpecificViewContact()
101 return std::make_unique
<sdr::contact::ViewContactOfSdrCircObj
>(*this);
104 SdrCircKind
ToSdrCircKind(SdrObjKind eKind
)
108 case SdrObjKind::CircleOrEllipse
: return SdrCircKind::Full
;
109 case SdrObjKind::CircleSection
: return SdrCircKind::Section
;
110 case SdrObjKind::CircleArc
: return SdrCircKind::Arc
;
111 case SdrObjKind::CircleCut
: return SdrCircKind::Cut
;
112 default: assert(false);
114 return SdrCircKind::Full
;
117 SdrCircObj::SdrCircObj(
119 SdrCircKind eNewKind
)
120 : SdrRectObj(rSdrModel
)
122 nStartAngle
=0_deg100
;
123 nEndAngle
=36000_deg100
;
124 meCircleKind
=eNewKind
;
125 m_bClosedObj
=eNewKind
!=SdrCircKind::Arc
;
128 SdrCircObj::SdrCircObj(SdrModel
& rSdrModel
, SdrCircObj
const & rSource
)
129 : SdrRectObj(rSdrModel
, rSource
)
131 meCircleKind
= rSource
.meCircleKind
;
132 nStartAngle
= rSource
.nStartAngle
;
133 nEndAngle
= rSource
.nEndAngle
;
134 m_bClosedObj
= rSource
.m_bClosedObj
;
137 SdrCircObj::SdrCircObj(
139 SdrCircKind eNewKind
,
140 const tools::Rectangle
& rRect
)
141 : SdrRectObj(rSdrModel
, rRect
)
143 nStartAngle
=0_deg100
;
144 nEndAngle
=36000_deg100
;
145 meCircleKind
=eNewKind
;
146 m_bClosedObj
=eNewKind
!=SdrCircKind::Arc
;
149 SdrCircObj::SdrCircObj(
151 SdrCircKind eNewKind
,
152 const tools::Rectangle
& rRect
,
153 Degree100 nNewStartAngle
,
154 Degree100 nNewEndAngle
)
155 : SdrRectObj(rSdrModel
, rRect
)
157 Degree100 nAngleDif
=nNewEndAngle
-nNewStartAngle
;
158 nStartAngle
=NormAngle36000(nNewStartAngle
);
159 nEndAngle
=NormAngle36000(nNewEndAngle
);
160 if (nAngleDif
==36000_deg100
) nEndAngle
+=nAngleDif
; // full circle
161 meCircleKind
=eNewKind
;
162 m_bClosedObj
=eNewKind
!=SdrCircKind::Arc
;
165 SdrCircObj::~SdrCircObj()
169 void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec
& rInfo
) const
171 bool bCanConv
=!HasText() || ImpCanConvTextToCurve();
172 rInfo
.bEdgeRadiusAllowed
= false;
173 rInfo
.bCanConvToPath
=bCanConv
;
174 rInfo
.bCanConvToPoly
=bCanConv
;
175 rInfo
.bCanConvToContour
= !IsFontwork() && (rInfo
.bCanConvToPoly
|| LineGeometryUsageIsNecessary());
178 SdrObjKind
SdrCircObj::GetObjIdentifier() const
180 switch (meCircleKind
)
182 case SdrCircKind::Full
: return SdrObjKind::CircleOrEllipse
;
183 case SdrCircKind::Section
: return SdrObjKind::CircleSection
;
184 case SdrCircKind::Cut
: return SdrObjKind::CircleCut
;
185 case SdrCircKind::Arc
: return SdrObjKind::CircleArc
;
186 default: assert(false);
188 return SdrObjKind::CircleOrEllipse
;
191 bool SdrCircObj::PaintNeedsXPolyCirc() const
193 // XPoly is necessary for all rotated ellipse objects, circle and
195 // If not WIN, then (for now) also for circle/ellipse segments and circle/
196 // ellipse arcs (for precision)
197 bool bNeed
= maGeo
.nRotationAngle
|| maGeo
.nShearAngle
|| meCircleKind
== SdrCircKind::Cut
;
198 // If not WIN, then for everything except full circle (for now!)
199 if (meCircleKind
!=SdrCircKind::Full
) bNeed
= true;
201 const SfxItemSet
& rSet
= GetObjectItemSet();
204 // XPoly is necessary for everything that isn't LineSolid or LineNone
205 drawing::LineStyle eLine
= rSet
.Get(XATTR_LINESTYLE
).GetValue();
206 bNeed
= eLine
!= drawing::LineStyle_NONE
&& eLine
!= drawing::LineStyle_SOLID
;
208 // XPoly is necessary for thick lines
209 if(!bNeed
&& eLine
!= drawing::LineStyle_NONE
)
210 bNeed
= rSet
.Get(XATTR_LINEWIDTH
).GetValue() != 0;
212 // XPoly is necessary for circle arcs with line ends
213 if(!bNeed
&& meCircleKind
== SdrCircKind::Arc
)
215 // start of the line is here if StartPolygon, StartWidth!=0
216 bNeed
=rSet
.Get(XATTR_LINESTART
).GetLineStartValue().count() != 0 &&
217 rSet
.Get(XATTR_LINESTARTWIDTH
).GetValue() != 0;
221 // end of the line is here if EndPolygon, EndWidth!=0
222 bNeed
= rSet
.Get(XATTR_LINEEND
).GetLineEndValue().count() != 0 &&
223 rSet
.Get(XATTR_LINEENDWIDTH
).GetValue() != 0;
228 // XPoly is necessary if Fill !=None and !=Solid
229 if(!bNeed
&& meCircleKind
!= SdrCircKind::Arc
)
231 drawing::FillStyle eFill
=rSet
.Get(XATTR_FILLSTYLE
).GetValue();
232 bNeed
= eFill
!= drawing::FillStyle_NONE
&& eFill
!= drawing::FillStyle_SOLID
;
235 if(!bNeed
&& meCircleKind
!= SdrCircKind::Full
&& nStartAngle
== nEndAngle
)
236 bNeed
= true; // otherwise we're drawing a full circle
241 basegfx::B2DPolygon
SdrCircObj::ImpCalcXPolyCirc(const SdrCircKind eCircleKind
, const tools::Rectangle
& rRect1
, Degree100 nStart
, Degree100 nEnd
) const
243 const basegfx::B2DRange aRange
= vcl::unotools::b2DRectangleFromRectangle(rRect1
);
244 basegfx::B2DPolygon aCircPolygon
;
246 if(SdrCircKind::Full
== eCircleKind
)
248 // create full circle. Do not use createPolygonFromEllipse; it's necessary
249 // to get the start point to the bottom of the circle to keep compatible to
250 // old geometry creation
251 aCircPolygon
= basegfx::utils::createPolygonFromUnitCircle(1);
253 // needs own scaling and translation from unit circle to target size (same as
254 // would be in createPolygonFromEllipse)
255 const basegfx::B2DPoint
aCenter(aRange
.getCenter());
256 const basegfx::B2DHomMatrix
aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
257 aRange
.getWidth() / 2.0, aRange
.getHeight() / 2.0,
258 aCenter
.getX(), aCenter
.getY()));
259 aCircPolygon
.transform(aMatrix
);
263 // mirror start, end for geometry creation since model coordinate system is mirrored in Y
264 const double fStart(toRadians((36000_deg100
- nEnd
) % 36000_deg100
));
265 const double fEnd(toRadians((36000_deg100
- nStart
) % 36000_deg100
));
267 // create circle segment. This is not closed by default
268 aCircPolygon
= basegfx::utils::createPolygonFromEllipseSegment(
269 aRange
.getCenter(), aRange
.getWidth() / 2.0, aRange
.getHeight() / 2.0,
272 // check closing states
273 const bool bCloseSegment(SdrCircKind::Arc
!= eCircleKind
);
274 const bool bCloseUsingCenter(SdrCircKind::Section
== eCircleKind
);
278 if(bCloseUsingCenter
)
280 // add center point at start (for historical reasons)
281 basegfx::B2DPolygon aSector
;
282 aSector
.append(aRange
.getCenter());
283 aSector
.append(aCircPolygon
);
284 aCircPolygon
= aSector
;
288 aCircPolygon
.setClosed(true);
293 if (maGeo
.nShearAngle
|| maGeo
.nRotationAngle
)
295 // translate top left to (0,0)
296 const basegfx::B2DPoint
aTopLeft(aRange
.getMinimum());
297 basegfx::B2DHomMatrix
aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
298 -aTopLeft
.getX(), -aTopLeft
.getY()));
300 // shear, rotate and back to top left (if needed)
301 aMatrix
= basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
302 -maGeo
.mfTanShearAngle
,
303 maGeo
.nRotationAngle
? toRadians(36000_deg100
- maGeo
.nRotationAngle
) : 0.0,
306 // apply transformation
307 aCircPolygon
.transform(aMatrix
);
313 void SdrCircObj::RecalcXPoly()
315 basegfx::B2DPolygon
aPolyCirc(ImpCalcXPolyCirc(meCircleKind
, getRectangle(), nStartAngle
, nEndAngle
));
316 mpXPoly
= XPolygon(aPolyCirc
);
319 OUString
SdrCircObj::TakeObjNameSingul() const
321 TranslateId pID
=STR_ObjNameSingulCIRC
;
322 if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo
.nShearAngle
== 0_deg100
)
324 switch (meCircleKind
) {
325 case SdrCircKind::Full
: pID
=STR_ObjNameSingulCIRC
; break;
326 case SdrCircKind::Section
: pID
=STR_ObjNameSingulSECT
; break;
327 case SdrCircKind::Arc
: pID
=STR_ObjNameSingulCARC
; break;
328 case SdrCircKind::Cut
: pID
=STR_ObjNameSingulCCUT
; break;
332 switch (meCircleKind
) {
333 case SdrCircKind::Full
: pID
=STR_ObjNameSingulCIRCE
; break;
334 case SdrCircKind::Section
: pID
=STR_ObjNameSingulSECTE
; break;
335 case SdrCircKind::Arc
: pID
=STR_ObjNameSingulCARCE
; break;
336 case SdrCircKind::Cut
: pID
=STR_ObjNameSingulCCUTE
; break;
340 OUString
sName(SvxResId(pID
));
342 OUString
aName(GetName());
343 if (!aName
.isEmpty())
344 sName
+= " '" + aName
+ "'";
348 OUString
SdrCircObj::TakeObjNamePlural() const
350 TranslateId pID
=STR_ObjNamePluralCIRC
;
351 if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo
.nShearAngle
== 0_deg100
)
353 switch (meCircleKind
) {
354 case SdrCircKind::Full
: pID
=STR_ObjNamePluralCIRC
; break;
355 case SdrCircKind::Section
: pID
=STR_ObjNamePluralSECT
; break;
356 case SdrCircKind::Arc
: pID
=STR_ObjNamePluralCARC
; break;
357 case SdrCircKind::Cut
: pID
=STR_ObjNamePluralCCUT
; break;
361 switch (meCircleKind
) {
362 case SdrCircKind::Full
: pID
=STR_ObjNamePluralCIRCE
; break;
363 case SdrCircKind::Section
: pID
=STR_ObjNamePluralSECTE
; break;
364 case SdrCircKind::Arc
: pID
=STR_ObjNamePluralCARCE
; break;
365 case SdrCircKind::Cut
: pID
=STR_ObjNamePluralCCUTE
; break;
369 return SvxResId(pID
);
372 rtl::Reference
<SdrObject
> SdrCircObj::CloneSdrObject(SdrModel
& rTargetModel
) const
374 return new SdrCircObj(rTargetModel
, *this);
377 basegfx::B2DPolyPolygon
SdrCircObj::TakeXorPoly() const
379 const basegfx::B2DPolygon
aCircPolygon(ImpCalcXPolyCirc(meCircleKind
, getRectangle(), nStartAngle
, nEndAngle
));
380 return basegfx::B2DPolyPolygon(aCircPolygon
);
385 struct ImpCircUser
: public SdrDragStatUserData
402 void SetCreateParams(SdrDragStat
const & rStat
);
407 sal_uInt32
SdrCircObj::GetHdlCount() const
409 if(SdrCircKind::Full
!= meCircleKind
)
419 void SdrCircObj::AddToHdlList(SdrHdlList
& rHdlList
) const
421 for (sal_uInt32 nHdlNum
=(SdrCircKind::Full
==meCircleKind
)?2:0; nHdlNum
<=9; ++nHdlNum
)
424 SdrHdlKind
eLocalKind(SdrHdlKind::Move
);
426 tools::Rectangle aRectangle
= getRectangle();
430 aPnt
= GetAnglePnt(aRectangle
, nStartAngle
);
431 eLocalKind
= SdrHdlKind::Circle
;
435 aPnt
= GetAnglePnt(aRectangle
, nEndAngle
);
436 eLocalKind
= SdrHdlKind::Circle
;
440 aPnt
= aRectangle
.TopLeft();
441 eLocalKind
= SdrHdlKind::UpperLeft
;
444 aPnt
= aRectangle
.TopCenter();
445 eLocalKind
= SdrHdlKind::Upper
;
448 aPnt
= aRectangle
.TopRight();
449 eLocalKind
= SdrHdlKind::UpperRight
;
452 aPnt
= aRectangle
.LeftCenter();
453 eLocalKind
= SdrHdlKind::Left
;
456 aPnt
= aRectangle
.RightCenter();
457 eLocalKind
= SdrHdlKind::Right
;
460 aPnt
= aRectangle
.BottomLeft();
461 eLocalKind
= SdrHdlKind::LowerLeft
;
464 aPnt
= aRectangle
.BottomCenter();
465 eLocalKind
= SdrHdlKind::Lower
;
468 aPnt
= aRectangle
.BottomRight();
469 eLocalKind
= SdrHdlKind::LowerRight
;
473 if (maGeo
.nShearAngle
)
475 ShearPoint(aPnt
, aRectangle
.TopLeft(), maGeo
.mfTanShearAngle
);
478 if (maGeo
.nRotationAngle
)
480 RotatePoint(aPnt
, aRectangle
.TopLeft(), maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
483 std::unique_ptr
<SdrHdl
> pH(new SdrHdl(aPnt
,eLocalKind
));
484 pH
->SetPointNum(nPNum
);
485 pH
->SetObj(const_cast<SdrCircObj
*>(this));
486 pH
->SetRotationAngle(maGeo
.nRotationAngle
);
487 rHdlList
.AddHdl(std::move(pH
));
492 bool SdrCircObj::hasSpecialDrag() const
497 bool SdrCircObj::beginSpecialDrag(SdrDragStat
& rDrag
) const
499 const bool bAngle(rDrag
.GetHdl() && SdrHdlKind::Circle
== rDrag
.GetHdl()->GetKind());
503 if(1 == rDrag
.GetHdl()->GetPointNum() || 2 == rDrag
.GetHdl()->GetPointNum())
511 return SdrTextObj::beginSpecialDrag(rDrag
);
514 bool SdrCircObj::applySpecialDrag(SdrDragStat
& rDrag
)
516 const bool bAngle(rDrag
.GetHdl() && SdrHdlKind::Circle
== rDrag
.GetHdl()->GetKind());
520 Point
aPt(rDrag
.GetNow());
522 if (maGeo
.nRotationAngle
)
523 RotatePoint(aPt
, getRectangle().TopLeft(), -maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
525 if (maGeo
.nShearAngle
)
526 ShearPoint(aPt
, getRectangle().TopLeft(), -maGeo
.mfTanShearAngle
);
528 aPt
-= getRectangle().Center();
530 tools::Long nWdt
= getRectangle().Right() - getRectangle().Left();
531 tools::Long nHgt
= getRectangle().Bottom() - getRectangle().Top();
535 aPt
.setY(BigMulDiv(aPt
.Y(),nWdt
,nHgt
) );
539 aPt
.setX(BigMulDiv(aPt
.X(),nHgt
,nWdt
) );
542 Degree100 nAngle
=NormAngle36000(GetAngle(aPt
));
544 if (rDrag
.GetView() && rDrag
.GetView()->IsAngleSnapEnabled())
546 Degree100 nSA
=rDrag
.GetView()->GetSnapAngle();
550 nAngle
+=nSA
/2_deg100
;
553 nAngle
=NormAngle36000(nAngle
);
557 if(1 == rDrag
.GetHdl()->GetPointNum())
559 nStartAngle
= nAngle
;
561 else if(2 == rDrag
.GetHdl()->GetPointNum())
566 SetBoundAndSnapRectsDirty();
568 ImpSetCircInfoToAttr();
575 return SdrTextObj::applySpecialDrag(rDrag
);
579 OUString
SdrCircObj::getSpecialDragComment(const SdrDragStat
& rDrag
) const
581 const bool bCreateComment(rDrag
.GetView() && this == rDrag
.GetView()->GetCreateObj());
585 OUStringBuffer
aBuf(ImpGetDescriptionStr(STR_ViewCreateObj
));
586 const sal_uInt32
nPointCount(rDrag
.GetPointCount());
588 if(SdrCircKind::Full
!= meCircleKind
&& nPointCount
> 2)
590 const ImpCircUser
* pU
= static_cast<const ImpCircUser
*>(rDrag
.GetUser());
604 aBuf
.append(SdrModel::GetAngleString(nAngle
));
608 return aBuf
.makeStringAndClear();
612 const bool bAngle(rDrag
.GetHdl() && SdrHdlKind::Circle
== rDrag
.GetHdl()->GetKind());
616 const Degree100
nAngle(1 == rDrag
.GetHdl()->GetPointNum() ? nStartAngle
: nEndAngle
);
618 return ImpGetDescriptionStr(STR_DragCircAngle
) +
620 SdrModel::GetAngleString(nAngle
) +
625 return SdrTextObj::getSpecialDragComment(rDrag
);
631 void ImpCircUser::SetCreateParams(SdrDragStat
const & rStat
)
633 rStat
.TakeCreateRect(aR
);
636 nWdt
=aR
.Right()-aR
.Left();
637 nHgt
=aR
.Bottom()-aR
.Top();
640 if (rStat
.GetPointCount()>2) {
641 Point
aP(rStat
.GetPoint(2)-aCenter
);
642 if (nWdt
==0) aP
.setX(0 );
643 if (nHgt
==0) aP
.setY(0 );
645 if (nHgt
!=0) aP
.setY(aP
.Y()*nWdt
/nHgt
);
647 if (nWdt
!=0) aP
.setX(aP
.X()*nHgt
/nWdt
);
649 nStart
=NormAngle36000(GetAngle(aP
));
650 if (rStat
.GetView()!=nullptr && rStat
.GetView()->IsAngleSnapEnabled()) {
651 Degree100 nSA
=rStat
.GetView()->GetSnapAngle();
652 if (nSA
) { // angle snapping
653 nStart
+=nSA
/2_deg100
;
656 nStart
=NormAngle36000(nStart
);
659 aP1
= GetAnglePnt(aR
,nStart
);
662 if (rStat
.GetPointCount()<=3)
665 Point
aP(rStat
.GetPoint(3)-aCenter
);
667 aP
.setY(BigMulDiv(aP
.Y(),nWdt
,nHgt
) );
669 aP
.setX(BigMulDiv(aP
.X(),nHgt
,nWdt
) );
671 nEnd
=NormAngle36000(GetAngle(aP
));
672 if (rStat
.GetView()!=nullptr && rStat
.GetView()->IsAngleSnapEnabled()) {
673 Degree100 nSA
=rStat
.GetView()->GetSnapAngle();
674 if (nSA
) { // angle snapping
678 nEnd
=NormAngle36000(nEnd
);
683 void SdrCircObj::ImpSetCreateParams(SdrDragStat
& rStat
)
685 ImpCircUser
* pU
=static_cast<ImpCircUser
*>(rStat
.GetUser());
688 rStat
.SetUser(std::unique_ptr
<ImpCircUser
>(pU
));
690 pU
->SetCreateParams(rStat
);
693 bool SdrCircObj::BegCreate(SdrDragStat
& rStat
)
695 rStat
.SetOrtho4Possible();
696 tools::Rectangle
aRect1(rStat
.GetStart(), rStat
.GetNow());
698 rStat
.SetActionRect(aRect1
);
699 setRectangle(aRect1
);
700 ImpSetCreateParams(rStat
);
704 bool SdrCircObj::MovCreate(SdrDragStat
& rStat
)
706 ImpSetCreateParams(rStat
);
707 ImpCircUser
* pU
=static_cast<ImpCircUser
*>(rStat
.GetUser());
708 rStat
.SetActionRect(pU
->aR
);
709 setRectangle(pU
->aR
); // for ObjName
710 ImpJustifyRect(maRectangle
);
711 nStartAngle
=pU
->nStart
;
714 m_bSnapRectDirty
=true;
717 // #i103058# push current angle settings to ItemSet to
718 // allow FullDrag visualisation
719 if(rStat
.GetPointCount() >= 4)
721 ImpSetCircInfoToAttr();
727 bool SdrCircObj::EndCreate(SdrDragStat
& rStat
, SdrCreateCmd eCmd
)
729 ImpSetCreateParams(rStat
);
730 ImpCircUser
* pU
=static_cast<ImpCircUser
*>(rStat
.GetUser());
732 if (eCmd
==SdrCreateCmd::ForceEnd
&& rStat
.GetPointCount()<4) meCircleKind
=SdrCircKind::Full
;
733 if (meCircleKind
==SdrCircKind::Full
) {
734 bRet
=rStat
.GetPointCount()>=2;
736 tools::Rectangle
aRectangle(pU
->aR
);
737 ImpJustifyRect(aRectangle
);
738 setRectangle(aRectangle
);
741 rStat
.SetNoSnap(rStat
.GetPointCount()>=2);
742 rStat
.SetOrtho4Possible(rStat
.GetPointCount()<2);
743 bRet
=rStat
.GetPointCount()>=4;
745 tools::Rectangle
aRectangle(pU
->aR
);
746 ImpJustifyRect(aRectangle
);
747 setRectangle(aRectangle
);
748 nStartAngle
=pU
->nStart
;
752 m_bClosedObj
=meCircleKind
!=SdrCircKind::Arc
;
753 SetBoundAndSnapRectsDirty();
755 ImpSetCircInfoToAttr();
757 rStat
.SetUser(nullptr);
761 void SdrCircObj::BrkCreate(SdrDragStat
& rStat
)
763 rStat
.SetUser(nullptr);
766 bool SdrCircObj::BckCreate(SdrDragStat
& rStat
)
768 rStat
.SetNoSnap(rStat
.GetPointCount()>=3);
769 rStat
.SetOrtho4Possible(rStat
.GetPointCount()<3);
770 return meCircleKind
!=SdrCircKind::Full
;
773 basegfx::B2DPolyPolygon
SdrCircObj::TakeCreatePoly(const SdrDragStat
& rDrag
) const
775 const ImpCircUser
* pU
= static_cast<const ImpCircUser
*>(rDrag
.GetUser());
777 if(rDrag
.GetPointCount() < 4)
779 // force to OBJ_CIRC to get full visualisation
780 basegfx::B2DPolyPolygon
aRetval(ImpCalcXPolyCirc(SdrCircKind::Full
, pU
->aR
, pU
->nStart
, pU
->nEnd
));
782 if(3 == rDrag
.GetPointCount())
784 // add edge to first point on ellipse
785 basegfx::B2DPolygon aNew
;
787 aNew
.append(basegfx::B2DPoint(pU
->aCenter
.X(), pU
->aCenter
.Y()));
788 aNew
.append(basegfx::B2DPoint(pU
->aP1
.X(), pU
->aP1
.Y()));
789 aRetval
.append(aNew
);
796 return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind
, pU
->aR
, pU
->nStart
, pU
->nEnd
));
800 PointerStyle
SdrCircObj::GetCreatePointer() const
802 switch (meCircleKind
) {
803 case SdrCircKind::Full
: return PointerStyle::DrawEllipse
;
804 case SdrCircKind::Section
: return PointerStyle::DrawPie
;
805 case SdrCircKind::Arc
: return PointerStyle::DrawArc
;
806 case SdrCircKind::Cut
: return PointerStyle::DrawCircleCut
;
809 return PointerStyle::Cross
;
812 void SdrCircObj::NbcMove(const Size
& aSize
)
814 moveRectangle(aSize
.Width(), aSize
.Height());
815 moveOutRectangle(aSize
.Width(), aSize
.Height());
816 maSnapRect
.Move(aSize
);
818 SetBoundAndSnapRectsDirty(true);
821 void SdrCircObj::NbcResize(const Point
& rRef
, const Fraction
& xFact
, const Fraction
& yFact
)
823 Degree100 nAngle0
= maGeo
.nRotationAngle
;
824 bool bNoShearRota
= (maGeo
.nRotationAngle
== 0_deg100
&& maGeo
.nShearAngle
== 0_deg100
);
825 SdrTextObj::NbcResize(rRef
,xFact
,yFact
);
826 bNoShearRota
|= (maGeo
.nRotationAngle
== 0_deg100
&& maGeo
.nShearAngle
== 0_deg100
);
827 if (meCircleKind
!=SdrCircKind::Full
) {
828 bool bXMirr
=(xFact
.GetNumerator()<0) != (xFact
.GetDenominator()<0);
829 bool bYMirr
=(yFact
.GetNumerator()<0) != (yFact
.GetDenominator()<0);
830 if (bXMirr
|| bYMirr
) {
831 // At bXMirr!=bYMirr we should actually swap both line ends.
832 // That, however, is pretty bad (because of forced "hard" formatting).
833 // Alternatively, we could implement a bMirrored flag (maybe even
834 // a more general one, e. g. for mirrored text, ...).
835 Degree100 nS0
=nStartAngle
;
836 Degree100 nE0
=nEndAngle
;
838 // the RectObj already mirrors at VMirror because of a 180deg rotation
839 if (! (bXMirr
&& bYMirr
)) {
841 nS0
=18000_deg100
-nE0
;
842 nE0
=18000_deg100
-nTmp
;
844 } else { // mirror contorted ellipses
845 if (bXMirr
!=bYMirr
) {
850 nS0
=18000_deg100
-nE0
;
851 nE0
=18000_deg100
-nTmp
;
858 nS0
-= maGeo
.nRotationAngle
;
859 nE0
-= maGeo
.nRotationAngle
;
862 Degree100 nAngleDif
=nE0
-nS0
;
863 nStartAngle
=NormAngle36000(nS0
);
864 nEndAngle
=NormAngle36000(nE0
);
865 if (nAngleDif
==36000_deg100
) nEndAngle
+=nAngleDif
; // full circle
869 ImpSetCircInfoToAttr();
872 void SdrCircObj::NbcShear(const Point
& rRef
, Degree100 nAngle
, double tn
, bool bVShear
)
874 SdrTextObj::NbcShear(rRef
,nAngle
,tn
,bVShear
);
876 ImpSetCircInfoToAttr();
879 void SdrCircObj::NbcMirror(const Point
& rRef1
, const Point
& rRef2
)
881 bool bFreeMirr
=meCircleKind
!=SdrCircKind::Full
;
884 if (bFreeMirr
) { // some preparations for using an arbitrary axis of reflection
885 Point
aCenter(getRectangle().Center());
886 tools::Long nWdt
= getRectangle().GetWidth() - 1;
887 tools::Long nHgt
= getRectangle().GetHeight() - 1;
888 tools::Long nMaxRad
=(std::max(nWdt
,nHgt
)+1) /2;
890 double a
= toRadians(nStartAngle
);
891 aTmpPt1
=Point(FRound(cos(a
)*nMaxRad
),-FRound(sin(a
)*nMaxRad
));
892 if (nWdt
==0) aTmpPt1
.setX(0 );
893 if (nHgt
==0) aTmpPt1
.setY(0 );
896 a
= toRadians(nEndAngle
);
897 aTmpPt2
=Point(FRound(cos(a
)*nMaxRad
),-FRound(sin(a
)*nMaxRad
));
898 if (nWdt
==0) aTmpPt2
.setX(0 );
899 if (nHgt
==0) aTmpPt2
.setY(0 );
901 if (maGeo
.nRotationAngle
)
903 RotatePoint(aTmpPt1
, getRectangle().TopLeft(), maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
904 RotatePoint(aTmpPt2
, getRectangle().TopLeft(), maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
906 if (maGeo
.nShearAngle
)
908 ShearPoint(aTmpPt1
, getRectangle().TopLeft(), maGeo
.mfTanShearAngle
);
909 ShearPoint(aTmpPt2
, getRectangle().TopLeft(), maGeo
.mfTanShearAngle
);
912 SdrTextObj::NbcMirror(rRef1
,rRef2
);
913 if (meCircleKind
!=SdrCircKind::Full
) { // adapt starting and finishing angle
914 MirrorPoint(aTmpPt1
,rRef1
,rRef2
);
915 MirrorPoint(aTmpPt2
,rRef1
,rRef2
);
917 if (maGeo
.nRotationAngle
)
919 RotatePoint(aTmpPt1
, getRectangle().TopLeft(), -maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
); // -sin for reversion
920 RotatePoint(aTmpPt2
, getRectangle().TopLeft(), -maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
); // -sin for reversion
923 if (maGeo
.nShearAngle
)
925 ShearPoint(aTmpPt1
, getRectangle().TopLeft(), -maGeo
.mfTanShearAngle
); // -tan for reversion
926 ShearPoint(aTmpPt2
, getRectangle().TopLeft(), -maGeo
.mfTanShearAngle
); // -tan for reversion
928 Point
aCenter(getRectangle().Center());
931 // because it's mirrored, the angles are swapped, too
932 nStartAngle
=GetAngle(aTmpPt2
);
933 nEndAngle
=GetAngle(aTmpPt1
);
934 Degree100 nAngleDif
=nEndAngle
-nStartAngle
;
935 nStartAngle
=NormAngle36000(nStartAngle
);
936 nEndAngle
=NormAngle36000(nEndAngle
);
937 if (nAngleDif
==36000_deg100
) nEndAngle
+=nAngleDif
; // full circle
940 ImpSetCircInfoToAttr();
943 std::unique_ptr
<SdrObjGeoData
> SdrCircObj::NewGeoData() const
945 return std::make_unique
<SdrCircObjGeoData
>();
948 void SdrCircObj::SaveGeoData(SdrObjGeoData
& rGeo
) const
950 SdrRectObj::SaveGeoData(rGeo
);
951 SdrCircObjGeoData
& rCGeo
=static_cast<SdrCircObjGeoData
&>(rGeo
);
952 rCGeo
.nStartAngle
=nStartAngle
;
953 rCGeo
.nEndAngle
=nEndAngle
;
956 void SdrCircObj::RestoreGeoData(const SdrObjGeoData
& rGeo
)
958 SdrRectObj::RestoreGeoData(rGeo
);
959 const SdrCircObjGeoData
& rCGeo
=static_cast<const SdrCircObjGeoData
&>(rGeo
);
960 nStartAngle
=rCGeo
.nStartAngle
;
961 nEndAngle
=rCGeo
.nEndAngle
;
963 ImpSetCircInfoToAttr();
966 static void Union(tools::Rectangle
& rR
, const Point
& rP
)
968 if (rP
.X()<rR
.Left ()) rR
.SetLeft(rP
.X() );
969 if (rP
.X()>rR
.Right ()) rR
.SetRight(rP
.X() );
970 if (rP
.Y()<rR
.Top ()) rR
.SetTop(rP
.Y() );
971 if (rP
.Y()>rR
.Bottom()) rR
.SetBottom(rP
.Y() );
974 void SdrCircObj::TakeUnrotatedSnapRect(tools::Rectangle
& rRect
) const
976 rRect
= getRectangle();
977 if (meCircleKind
!=SdrCircKind::Full
) {
978 const Point
aPntStart(GetAnglePnt(getRectangle(), nStartAngle
));
979 const Point
aPntEnd(GetAnglePnt(getRectangle(), nEndAngle
));
980 Degree100 a
=nStartAngle
;
981 Degree100 e
=nEndAngle
;
982 rRect
.SetLeft(getRectangle().Right() );
983 rRect
.SetRight(getRectangle().Left() );
984 rRect
.SetTop(getRectangle().Bottom() );
985 rRect
.SetBottom(getRectangle().Top() );
986 Union(rRect
,aPntStart
);
987 Union(rRect
,aPntEnd
);
988 if ((a
<=18000_deg100
&& e
>=18000_deg100
) || (a
>e
&& (a
<=18000_deg100
|| e
>=18000_deg100
))) {
989 Union(rRect
, getRectangle().LeftCenter());
991 if ((a
<=27000_deg100
&& e
>=27000_deg100
) || (a
>e
&& (a
<=27000_deg100
|| e
>=27000_deg100
))) {
992 Union(rRect
, getRectangle().BottomCenter());
995 Union(rRect
, getRectangle().RightCenter());
997 if ((a
<=9000_deg100
&& e
>=9000_deg100
) || (a
>e
&& (a
<=9000_deg100
|| e
>=9000_deg100
))) {
998 Union(rRect
, getRectangle().TopCenter());
1000 if (meCircleKind
==SdrCircKind::Section
) {
1001 Union(rRect
, getRectangle().Center());
1003 if (maGeo
.nRotationAngle
)
1005 Point
aDst(rRect
.TopLeft());
1006 aDst
-= getRectangle().TopLeft();
1008 RotatePoint(aDst
,Point(), maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
1010 rRect
.Move(aDst
.X(),aDst
.Y());
1013 if (maGeo
.nShearAngle
==0_deg100
)
1016 tools::Long nDst
= FRound((rRect
.Bottom() - rRect
.Top()) * maGeo
.mfTanShearAngle
);
1017 if (maGeo
.nShearAngle
> 0_deg100
)
1019 Point
aRef(rRect
.TopLeft());
1020 rRect
.AdjustLeft( -nDst
);
1021 Point
aTmpPt(rRect
.TopLeft());
1022 RotatePoint(aTmpPt
, aRef
, maGeo
.mfSinRotationAngle
, maGeo
.mfCosRotationAngle
);
1023 aTmpPt
-=rRect
.TopLeft();
1024 rRect
.Move(aTmpPt
.X(),aTmpPt
.Y());
1026 rRect
.AdjustRight( -nDst
);
1030 void SdrCircObj::RecalcSnapRect()
1032 if (PaintNeedsXPolyCirc()) {
1033 maSnapRect
=GetXPoly().GetBoundRect();
1035 TakeUnrotatedSnapRect(maSnapRect
);
1039 void SdrCircObj::NbcSetSnapRect(const tools::Rectangle
& rRect
)
1041 if (maGeo
.nRotationAngle
|| maGeo
.nShearAngle
|| meCircleKind
!= SdrCircKind::Full
)
1043 tools::Rectangle
aSR0(GetSnapRect());
1044 tools::Long nWdt0
=aSR0
.Right()-aSR0
.Left();
1045 tools::Long nHgt0
=aSR0
.Bottom()-aSR0
.Top();
1046 tools::Long nWdt1
=rRect
.Right()-rRect
.Left();
1047 tools::Long nHgt1
=rRect
.Bottom()-rRect
.Top();
1048 NbcResize(maSnapRect
.TopLeft(),Fraction(nWdt1
,nWdt0
),Fraction(nHgt1
,nHgt0
));
1049 NbcMove(Size(rRect
.Left()-aSR0
.Left(),rRect
.Top()-aSR0
.Top()));
1051 setRectangle(rRect
);
1052 ImpJustifyRect(maRectangle
);
1054 SetBoundAndSnapRectsDirty();
1056 ImpSetCircInfoToAttr();
1059 sal_uInt32
SdrCircObj::GetSnapPointCount() const
1061 if (meCircleKind
==SdrCircKind::Full
) {
1068 Point
SdrCircObj::GetSnapPoint(sal_uInt32 i
) const
1072 case 1 : return GetAnglePnt(getRectangle(), nStartAngle
);
1073 case 2 : return GetAnglePnt(getRectangle(), nEndAngle
);
1074 default: return getRectangle().Center();
1078 void SdrCircObj::Notify(SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
1081 SdrRectObj::Notify(rBC
,rHint
);
1082 ImpSetAttrToCircInfo();
1086 void SdrCircObj::ImpSetAttrToCircInfo()
1088 const SfxItemSet
& rSet
= GetObjectItemSet();
1089 SdrCircKind eNewKind
= rSet
.Get(SDRATTR_CIRCKIND
).GetValue();
1091 Degree100 nNewStart
= rSet
.Get(SDRATTR_CIRCSTARTANGLE
).GetValue();
1092 Degree100 nNewEnd
= rSet
.Get(SDRATTR_CIRCENDANGLE
).GetValue();
1094 bool bKindChg
= meCircleKind
!= eNewKind
;
1095 bool bAngleChg
= nNewStart
!= nStartAngle
|| nNewEnd
!= nEndAngle
;
1097 if(bKindChg
|| bAngleChg
)
1099 meCircleKind
= eNewKind
;
1100 nStartAngle
= nNewStart
;
1101 nEndAngle
= nNewEnd
;
1103 if(bKindChg
|| (meCircleKind
!= SdrCircKind::Full
&& bAngleChg
))
1106 SetBoundAndSnapRectsDirty();
1111 void SdrCircObj::ImpSetCircInfoToAttr()
1113 const SfxItemSet
& rSet
= GetObjectItemSet();
1115 SdrCircKind eOldKindA
= rSet
.Get(SDRATTR_CIRCKIND
).GetValue();
1116 Degree100 nOldStartAngle
= rSet
.Get(SDRATTR_CIRCSTARTANGLE
).GetValue();
1117 Degree100 nOldEndAngle
= rSet
.Get(SDRATTR_CIRCENDANGLE
).GetValue();
1119 if(meCircleKind
== eOldKindA
&& nStartAngle
== nOldStartAngle
&& nEndAngle
== nOldEndAngle
)
1122 // since SetItem() implicitly calls ImpSetAttrToCircInfo()
1123 // setting the item directly is necessary here.
1124 if(meCircleKind
!= eOldKindA
)
1126 GetProperties().SetObjectItemDirect(SdrCircKindItem(meCircleKind
));
1129 if(nStartAngle
!= nOldStartAngle
)
1131 GetProperties().SetObjectItemDirect(makeSdrCircStartAngleItem(nStartAngle
));
1134 if(nEndAngle
!= nOldEndAngle
)
1136 GetProperties().SetObjectItemDirect(makeSdrCircEndAngleItem(nEndAngle
));
1140 ImpSetAttrToCircInfo();
1143 rtl::Reference
<SdrObject
> SdrCircObj::DoConvertToPolyObj(bool bBezier
, bool bAddText
) const
1145 const bool bFill(meCircleKind
!= SdrCircKind::Arc
);
1146 const basegfx::B2DPolygon
aCircPolygon(ImpCalcXPolyCirc(meCircleKind
, getRectangle(), nStartAngle
, nEndAngle
));
1147 rtl::Reference
<SdrObject
> pRet
= ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon
), bFill
, bBezier
);
1151 pRet
= ImpConvertAddText(std::move(pRet
), bBezier
);
1157 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */