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
, tools::Long 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;
60 a
= nAngle
* F_PI18000
;
61 Point
aRetval(FRound(cos(a
)*nMaxRad
),-FRound(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 OBJ_CIRC
: return SdrCircKind::Full
;
110 case OBJ_SECT
: return SdrCircKind::Section
;
111 case OBJ_CARC
: return SdrCircKind::Arc
;
112 case OBJ_CCUT
: return SdrCircKind::Cut
;
113 default: assert(false);
115 return SdrCircKind::Full
;
118 SdrCircObj::SdrCircObj(
120 SdrCircKind eNewKind
)
121 : SdrRectObj(rSdrModel
)
125 meCircleKind
=eNewKind
;
126 bClosedObj
=eNewKind
!=SdrCircKind::Arc
;
129 SdrCircObj::SdrCircObj(
131 SdrCircKind eNewKind
,
132 const tools::Rectangle
& rRect
)
133 : SdrRectObj(rSdrModel
, rRect
)
137 meCircleKind
=eNewKind
;
138 bClosedObj
=eNewKind
!=SdrCircKind::Arc
;
141 SdrCircObj::SdrCircObj(
143 SdrCircKind eNewKind
,
144 const tools::Rectangle
& rRect
,
145 tools::Long nNewStartWink
,
146 tools::Long nNewEndWink
)
147 : SdrRectObj(rSdrModel
, rRect
)
149 tools::Long nAngleDif
=nNewEndWink
-nNewStartWink
;
150 nStartAngle
=NormAngle36000(nNewStartWink
);
151 nEndAngle
=NormAngle36000(nNewEndWink
);
152 if (nAngleDif
==36000) nEndAngle
+=nAngleDif
; // full circle
153 meCircleKind
=eNewKind
;
154 bClosedObj
=eNewKind
!=SdrCircKind::Arc
;
157 SdrCircObj::~SdrCircObj()
161 void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec
& rInfo
) const
163 bool bCanConv
=!HasText() || ImpCanConvTextToCurve();
164 rInfo
.bEdgeRadiusAllowed
= false;
165 rInfo
.bCanConvToPath
=bCanConv
;
166 rInfo
.bCanConvToPoly
=bCanConv
;
167 rInfo
.bCanConvToContour
= !IsFontwork() && (rInfo
.bCanConvToPoly
|| LineGeometryUsageIsNecessary());
170 SdrObjKind
SdrCircObj::GetObjIdentifier() const
172 switch (meCircleKind
)
174 case SdrCircKind::Full
: return OBJ_CIRC
;
175 case SdrCircKind::Section
: return OBJ_SECT
;
176 case SdrCircKind::Cut
: return OBJ_CCUT
;
177 case SdrCircKind::Arc
: return OBJ_CARC
;
178 default: assert(false);
183 bool SdrCircObj::PaintNeedsXPolyCirc() const
185 // XPoly is necessary for all rotated ellipse objects, circle and
187 // If not WIN, then (for now) also for circle/ellipse segments and circle/
188 // ellipse arcs (for precision)
189 bool bNeed
=aGeo
.nRotationAngle
!=0 || aGeo
.nShearAngle
!=0 || meCircleKind
==SdrCircKind::Cut
;
190 // If not WIN, then for everything except full circle (for now!)
191 if (meCircleKind
!=SdrCircKind::Full
) bNeed
= true;
193 const SfxItemSet
& rSet
= GetObjectItemSet();
196 // XPoly is necessary for everything that isn't LineSolid or LineNone
197 drawing::LineStyle eLine
= rSet
.Get(XATTR_LINESTYLE
).GetValue();
198 bNeed
= eLine
!= drawing::LineStyle_NONE
&& eLine
!= drawing::LineStyle_SOLID
;
200 // XPoly is necessary for thick lines
201 if(!bNeed
&& eLine
!= drawing::LineStyle_NONE
)
202 bNeed
= rSet
.Get(XATTR_LINEWIDTH
).GetValue() != 0;
204 // XPoly is necessary for circle arcs with line ends
205 if(!bNeed
&& meCircleKind
== SdrCircKind::Arc
)
207 // start of the line is here if StartPolygon, StartWidth!=0
208 bNeed
=rSet
.Get(XATTR_LINESTART
).GetLineStartValue().count() != 0 &&
209 rSet
.Get(XATTR_LINESTARTWIDTH
).GetValue() != 0;
213 // end of the line is here if EndPolygon, EndWidth!=0
214 bNeed
= rSet
.Get(XATTR_LINEEND
).GetLineEndValue().count() != 0 &&
215 rSet
.Get(XATTR_LINEENDWIDTH
).GetValue() != 0;
220 // XPoly is necessary if Fill !=None and !=Solid
221 if(!bNeed
&& meCircleKind
!= SdrCircKind::Arc
)
223 drawing::FillStyle eFill
=rSet
.Get(XATTR_FILLSTYLE
).GetValue();
224 bNeed
= eFill
!= drawing::FillStyle_NONE
&& eFill
!= drawing::FillStyle_SOLID
;
227 if(!bNeed
&& meCircleKind
!= SdrCircKind::Full
&& nStartAngle
== nEndAngle
)
228 bNeed
= true; // otherwise we're drawing a full circle
233 basegfx::B2DPolygon
SdrCircObj::ImpCalcXPolyCirc(const SdrCircKind eCircleKind
, const tools::Rectangle
& rRect1
, tools::Long nStart
, tools::Long nEnd
) const
235 const basegfx::B2DRange aRange
= vcl::unotools::b2DRectangleFromRectangle(rRect1
);
236 basegfx::B2DPolygon aCircPolygon
;
238 if(SdrCircKind::Full
== eCircleKind
)
240 // create full circle. Do not use createPolygonFromEllipse; it's necessary
241 // to get the start point to the bottom of the circle to keep compatible to
242 // old geometry creation
243 aCircPolygon
= basegfx::utils::createPolygonFromUnitCircle(1);
245 // needs own scaling and translation from unit circle to target size (same as
246 // would be in createPolygonFromEllipse)
247 const basegfx::B2DPoint
aCenter(aRange
.getCenter());
248 const basegfx::B2DHomMatrix
aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
249 aRange
.getWidth() / 2.0, aRange
.getHeight() / 2.0,
250 aCenter
.getX(), aCenter
.getY()));
251 aCircPolygon
.transform(aMatrix
);
255 // mirror start, end for geometry creation since model coordinate system is mirrored in Y
256 // #i111715# increase numerical correctness by first dividing and not using F_PI1800
257 const double fStart((((36000 - nEnd
) % 36000) / 18000.0) * F_PI
);
258 const double fEnd((((36000 - nStart
) % 36000) / 18000.0) * F_PI
);
260 // create circle segment. This is not closed by default
261 aCircPolygon
= basegfx::utils::createPolygonFromEllipseSegment(
262 aRange
.getCenter(), aRange
.getWidth() / 2.0, aRange
.getHeight() / 2.0,
265 // check closing states
266 const bool bCloseSegment(SdrCircKind::Arc
!= eCircleKind
);
267 const bool bCloseUsingCenter(SdrCircKind::Section
== eCircleKind
);
271 if(bCloseUsingCenter
)
273 // add center point at start (for historical reasons)
274 basegfx::B2DPolygon aSector
;
275 aSector
.append(aRange
.getCenter());
276 aSector
.append(aCircPolygon
);
277 aCircPolygon
= aSector
;
281 aCircPolygon
.setClosed(true);
286 if(aGeo
.nShearAngle
|| aGeo
.nRotationAngle
)
288 // translate top left to (0,0)
289 const basegfx::B2DPoint
aTopLeft(aRange
.getMinimum());
290 basegfx::B2DHomMatrix
aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
291 -aTopLeft
.getX(), -aTopLeft
.getY()));
293 // shear, rotate and back to top left (if needed)
294 aMatrix
= basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
295 aGeo
.nShearAngle
? tan((36000 - aGeo
.nShearAngle
) * F_PI18000
) : 0.0,
296 aGeo
.nRotationAngle
? (36000 - aGeo
.nRotationAngle
) * F_PI18000
: 0.0,
299 // apply transformation
300 aCircPolygon
.transform(aMatrix
);
306 void SdrCircObj::RecalcXPoly()
308 const basegfx::B2DPolygon
aPolyCirc(ImpCalcXPolyCirc(meCircleKind
, maRect
, nStartAngle
, nEndAngle
));
309 mpXPoly
.reset( new XPolygon(aPolyCirc
) );
312 OUString
SdrCircObj::TakeObjNameSingul() const
314 const char* pID
=STR_ObjNameSingulCIRC
;
315 if (maRect
.GetWidth() == maRect
.GetHeight() && aGeo
.nShearAngle
==0)
317 switch (meCircleKind
) {
318 case SdrCircKind::Full
: pID
=STR_ObjNameSingulCIRC
; break;
319 case SdrCircKind::Section
: pID
=STR_ObjNameSingulSECT
; break;
320 case SdrCircKind::Arc
: pID
=STR_ObjNameSingulCARC
; break;
321 case SdrCircKind::Cut
: pID
=STR_ObjNameSingulCCUT
; break;
325 switch (meCircleKind
) {
326 case SdrCircKind::Full
: pID
=STR_ObjNameSingulCIRCE
; break;
327 case SdrCircKind::Section
: pID
=STR_ObjNameSingulSECTE
; break;
328 case SdrCircKind::Arc
: pID
=STR_ObjNameSingulCARCE
; break;
329 case SdrCircKind::Cut
: pID
=STR_ObjNameSingulCCUTE
; break;
333 OUStringBuffer
sName(SvxResId(pID
));
335 OUString
aName(GetName());
336 if (!aName
.isEmpty())
343 return sName
.makeStringAndClear();
346 OUString
SdrCircObj::TakeObjNamePlural() const
348 const char* pID
=STR_ObjNamePluralCIRC
;
349 if (maRect
.GetWidth() == maRect
.GetHeight() && aGeo
.nShearAngle
==0)
351 switch (meCircleKind
) {
352 case SdrCircKind::Full
: pID
=STR_ObjNamePluralCIRC
; break;
353 case SdrCircKind::Section
: pID
=STR_ObjNamePluralSECT
; break;
354 case SdrCircKind::Arc
: pID
=STR_ObjNamePluralCARC
; break;
355 case SdrCircKind::Cut
: pID
=STR_ObjNamePluralCCUT
; break;
359 switch (meCircleKind
) {
360 case SdrCircKind::Full
: pID
=STR_ObjNamePluralCIRCE
; break;
361 case SdrCircKind::Section
: pID
=STR_ObjNamePluralSECTE
; break;
362 case SdrCircKind::Arc
: pID
=STR_ObjNamePluralCARCE
; break;
363 case SdrCircKind::Cut
: pID
=STR_ObjNamePluralCCUTE
; break;
367 return SvxResId(pID
);
370 SdrCircObj
* SdrCircObj::CloneSdrObject(SdrModel
& rTargetModel
) const
372 return CloneHelper
< SdrCircObj
>(rTargetModel
);
375 SdrCircObj
& SdrCircObj::operator=(const SdrCircObj
& rObj
)
379 SdrRectObj::operator=(rObj
);
381 meCircleKind
= rObj
.meCircleKind
;
382 nStartAngle
= rObj
.nStartAngle
;
383 nEndAngle
= rObj
.nEndAngle
;
388 basegfx::B2DPolyPolygon
SdrCircObj::TakeXorPoly() const
390 const basegfx::B2DPolygon
aCircPolygon(ImpCalcXPolyCirc(meCircleKind
, maRect
, nStartAngle
, nEndAngle
));
391 return basegfx::B2DPolyPolygon(aCircPolygon
);
396 struct ImpCircUser
: public SdrDragStatUserData
413 void SetCreateParams(SdrDragStat
const & rStat
);
418 sal_uInt32
SdrCircObj::GetHdlCount() const
420 if(SdrCircKind::Full
!= meCircleKind
)
430 void SdrCircObj::AddToHdlList(SdrHdlList
& rHdlList
) const
432 for (sal_uInt32 nHdlNum
=(SdrCircKind::Full
==meCircleKind
)?2:0; nHdlNum
<=9; ++nHdlNum
)
435 SdrHdlKind
eLocalKind(SdrHdlKind::Move
);
441 aPnt
= GetAnglePnt(maRect
,nStartAngle
);
442 eLocalKind
= SdrHdlKind::Circle
;
446 aPnt
= GetAnglePnt(maRect
,nEndAngle
);
447 eLocalKind
= SdrHdlKind::Circle
;
451 aPnt
= maRect
.TopLeft();
452 eLocalKind
= SdrHdlKind::UpperLeft
;
455 aPnt
= maRect
.TopCenter();
456 eLocalKind
= SdrHdlKind::Upper
;
459 aPnt
= maRect
.TopRight();
460 eLocalKind
= SdrHdlKind::UpperRight
;
463 aPnt
= maRect
.LeftCenter();
464 eLocalKind
= SdrHdlKind::Left
;
467 aPnt
= maRect
.RightCenter();
468 eLocalKind
= SdrHdlKind::Right
;
471 aPnt
= maRect
.BottomLeft();
472 eLocalKind
= SdrHdlKind::LowerLeft
;
475 aPnt
= maRect
.BottomCenter();
476 eLocalKind
= SdrHdlKind::Lower
;
479 aPnt
= maRect
.BottomRight();
480 eLocalKind
= SdrHdlKind::LowerRight
;
484 if (aGeo
.nShearAngle
)
486 ShearPoint(aPnt
,maRect
.TopLeft(),aGeo
.nTan
);
489 if (aGeo
.nRotationAngle
)
491 RotatePoint(aPnt
,maRect
.TopLeft(),aGeo
.nSin
,aGeo
.nCos
);
494 std::unique_ptr
<SdrHdl
> pH(new SdrHdl(aPnt
,eLocalKind
));
495 pH
->SetPointNum(nPNum
);
496 pH
->SetObj(const_cast<SdrCircObj
*>(this));
497 pH
->SetRotationAngle(aGeo
.nRotationAngle
);
498 rHdlList
.AddHdl(std::move(pH
));
503 bool SdrCircObj::hasSpecialDrag() const
508 bool SdrCircObj::beginSpecialDrag(SdrDragStat
& rDrag
) const
510 const bool bAngle(rDrag
.GetHdl() && SdrHdlKind::Circle
== rDrag
.GetHdl()->GetKind());
514 if(1 == rDrag
.GetHdl()->GetPointNum() || 2 == rDrag
.GetHdl()->GetPointNum())
522 return SdrTextObj::beginSpecialDrag(rDrag
);
525 bool SdrCircObj::applySpecialDrag(SdrDragStat
& rDrag
)
527 const bool bAngle(rDrag
.GetHdl() && SdrHdlKind::Circle
== rDrag
.GetHdl()->GetKind());
531 Point
aPt(rDrag
.GetNow());
533 if (aGeo
.nRotationAngle
!=0)
534 RotatePoint(aPt
,maRect
.TopLeft(),-aGeo
.nSin
,aGeo
.nCos
);
536 if (aGeo
.nShearAngle
!=0)
537 ShearPoint(aPt
,maRect
.TopLeft(),-aGeo
.nTan
);
539 aPt
-= maRect
.Center();
541 tools::Long nWdt
= maRect
.Right() - maRect
.Left();
542 tools::Long nHgt
= maRect
.Bottom() - maRect
.Top();
546 aPt
.setY(BigMulDiv(aPt
.Y(),nWdt
,nHgt
) );
550 aPt
.setX(BigMulDiv(aPt
.X(),nHgt
,nWdt
) );
553 tools::Long nAngle
=NormAngle36000(GetAngle(aPt
));
555 if (rDrag
.GetView() && rDrag
.GetView()->IsAngleSnapEnabled())
557 tools::Long nSA
=rDrag
.GetView()->GetSnapAngle();
564 nAngle
=NormAngle36000(nAngle
);
568 if(1 == rDrag
.GetHdl()->GetPointNum())
570 nStartAngle
= nAngle
;
572 else if(2 == rDrag
.GetHdl()->GetPointNum())
579 ImpSetCircInfoToAttr();
586 return SdrTextObj::applySpecialDrag(rDrag
);
590 OUString
SdrCircObj::getSpecialDragComment(const SdrDragStat
& rDrag
) const
592 const bool bCreateComment(rDrag
.GetView() && this == rDrag
.GetView()->GetCreateObj());
596 OUStringBuffer
aBuf(ImpGetDescriptionStr(STR_ViewCreateObj
));
597 const sal_uInt32
nPointCount(rDrag
.GetPointCount());
599 if(SdrCircKind::Full
!= meCircleKind
&& nPointCount
> 2)
601 const ImpCircUser
* pU
= static_cast<const ImpCircUser
*>(rDrag
.GetUser());
615 aBuf
.append(SdrModel::GetAngleString(nAngle
));
619 return aBuf
.makeStringAndClear();
623 const bool bAngle(rDrag
.GetHdl() && SdrHdlKind::Circle
== rDrag
.GetHdl()->GetKind());
627 const sal_Int32
nAngle(1 == rDrag
.GetHdl()->GetPointNum() ? nStartAngle
: nEndAngle
);
629 OUStringBuffer
aBuf(ImpGetDescriptionStr(STR_DragCircAngle
));
631 aBuf
.append(SdrModel::GetAngleString(nAngle
));
634 return aBuf
.makeStringAndClear();
638 return SdrTextObj::getSpecialDragComment(rDrag
);
644 void ImpCircUser::SetCreateParams(SdrDragStat
const & rStat
)
646 rStat
.TakeCreateRect(aR
);
649 nWdt
=aR
.Right()-aR
.Left();
650 nHgt
=aR
.Bottom()-aR
.Top();
653 if (rStat
.GetPointCount()>2) {
654 Point
aP(rStat
.GetPoint(2)-aCenter
);
655 if (nWdt
==0) aP
.setX(0 );
656 if (nHgt
==0) aP
.setY(0 );
658 if (nHgt
!=0) aP
.setY(aP
.Y()*nWdt
/nHgt
);
660 if (nWdt
!=0) aP
.setX(aP
.X()*nHgt
/nWdt
);
662 nStart
=NormAngle36000(GetAngle(aP
));
663 if (rStat
.GetView()!=nullptr && rStat
.GetView()->IsAngleSnapEnabled()) {
664 tools::Long nSA
=rStat
.GetView()->GetSnapAngle();
665 if (nSA
!=0) { // angle snapping
669 nStart
=NormAngle36000(nStart
);
672 aP1
= GetAnglePnt(aR
,nStart
);
675 if (rStat
.GetPointCount()<=3)
678 Point
aP(rStat
.GetPoint(3)-aCenter
);
680 aP
.setY(BigMulDiv(aP
.Y(),nWdt
,nHgt
) );
682 aP
.setX(BigMulDiv(aP
.X(),nHgt
,nWdt
) );
684 nEnd
=NormAngle36000(GetAngle(aP
));
685 if (rStat
.GetView()!=nullptr && rStat
.GetView()->IsAngleSnapEnabled()) {
686 tools::Long nSA
=rStat
.GetView()->GetSnapAngle();
687 if (nSA
!=0) { // angle snapping
691 nEnd
=NormAngle36000(nEnd
);
696 void SdrCircObj::ImpSetCreateParams(SdrDragStat
& rStat
)
698 ImpCircUser
* pU
=static_cast<ImpCircUser
*>(rStat
.GetUser());
701 rStat
.SetUser(std::unique_ptr
<ImpCircUser
>(pU
));
703 pU
->SetCreateParams(rStat
);
706 bool SdrCircObj::BegCreate(SdrDragStat
& rStat
)
708 rStat
.SetOrtho4Possible();
709 tools::Rectangle
aRect1(rStat
.GetStart(), rStat
.GetNow());
711 rStat
.SetActionRect(aRect1
);
713 ImpSetCreateParams(rStat
);
717 bool SdrCircObj::MovCreate(SdrDragStat
& rStat
)
719 ImpSetCreateParams(rStat
);
720 ImpCircUser
* pU
=static_cast<ImpCircUser
*>(rStat
.GetUser());
721 rStat
.SetActionRect(pU
->aR
);
722 maRect
= pU
->aR
; // for ObjName
723 ImpJustifyRect(maRect
);
724 nStartAngle
=pU
->nStart
;
730 // #i103058# push current angle settings to ItemSet to
731 // allow FullDrag visualisation
732 if(rStat
.GetPointCount() >= 4)
734 ImpSetCircInfoToAttr();
740 bool SdrCircObj::EndCreate(SdrDragStat
& rStat
, SdrCreateCmd eCmd
)
742 ImpSetCreateParams(rStat
);
743 ImpCircUser
* pU
=static_cast<ImpCircUser
*>(rStat
.GetUser());
745 if (eCmd
==SdrCreateCmd::ForceEnd
&& rStat
.GetPointCount()<4) meCircleKind
=SdrCircKind::Full
;
746 if (meCircleKind
==SdrCircKind::Full
) {
747 bRet
=rStat
.GetPointCount()>=2;
750 ImpJustifyRect(maRect
);
753 rStat
.SetNoSnap(rStat
.GetPointCount()>=2);
754 rStat
.SetOrtho4Possible(rStat
.GetPointCount()<2);
755 bRet
=rStat
.GetPointCount()>=4;
758 ImpJustifyRect(maRect
);
759 nStartAngle
=pU
->nStart
;
763 bClosedObj
=meCircleKind
!=SdrCircKind::Arc
;
766 ImpSetCircInfoToAttr();
768 rStat
.SetUser(nullptr);
772 void SdrCircObj::BrkCreate(SdrDragStat
& rStat
)
774 rStat
.SetUser(nullptr);
777 bool SdrCircObj::BckCreate(SdrDragStat
& rStat
)
779 rStat
.SetNoSnap(rStat
.GetPointCount()>=3);
780 rStat
.SetOrtho4Possible(rStat
.GetPointCount()<3);
781 return meCircleKind
!=SdrCircKind::Full
;
784 basegfx::B2DPolyPolygon
SdrCircObj::TakeCreatePoly(const SdrDragStat
& rDrag
) const
786 const ImpCircUser
* pU
= static_cast<const ImpCircUser
*>(rDrag
.GetUser());
788 if(rDrag
.GetPointCount() < 4)
790 // force to OBJ_CIRC to get full visualisation
791 basegfx::B2DPolyPolygon
aRetval(ImpCalcXPolyCirc(SdrCircKind::Full
, pU
->aR
, pU
->nStart
, pU
->nEnd
));
793 if(3 == rDrag
.GetPointCount())
795 // add edge to first point on ellipse
796 basegfx::B2DPolygon aNew
;
798 aNew
.append(basegfx::B2DPoint(pU
->aCenter
.X(), pU
->aCenter
.Y()));
799 aNew
.append(basegfx::B2DPoint(pU
->aP1
.X(), pU
->aP1
.Y()));
800 aRetval
.append(aNew
);
807 return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind
, pU
->aR
, pU
->nStart
, pU
->nEnd
));
811 PointerStyle
SdrCircObj::GetCreatePointer() const
813 switch (meCircleKind
) {
814 case SdrCircKind::Full
: return PointerStyle::DrawEllipse
;
815 case SdrCircKind::Section
: return PointerStyle::DrawPie
;
816 case SdrCircKind::Arc
: return PointerStyle::DrawArc
;
817 case SdrCircKind::Cut
: return PointerStyle::DrawCircleCut
;
820 return PointerStyle::Cross
;
823 void SdrCircObj::NbcMove(const Size
& aSiz
)
827 maSnapRect
.Move(aSiz
);
832 void SdrCircObj::NbcResize(const Point
& rRef
, const Fraction
& xFact
, const Fraction
& yFact
)
834 tools::Long nAngle0
=aGeo
.nRotationAngle
;
835 bool bNoShearRota
=(aGeo
.nRotationAngle
==0 && aGeo
.nShearAngle
==0);
836 SdrTextObj::NbcResize(rRef
,xFact
,yFact
);
837 bNoShearRota
|=(aGeo
.nRotationAngle
==0 && aGeo
.nShearAngle
==0);
838 if (meCircleKind
!=SdrCircKind::Full
) {
839 bool bXMirr
=(xFact
.GetNumerator()<0) != (xFact
.GetDenominator()<0);
840 bool bYMirr
=(yFact
.GetNumerator()<0) != (yFact
.GetDenominator()<0);
841 if (bXMirr
|| bYMirr
) {
842 // At bXMirr!=bYMirr we should actually swap both line ends.
843 // That, however, is pretty bad (because of forced "hard" formatting).
844 // Alternatively, we could implement a bMirrored flag (maybe even
845 // a more general one, e. g. for mirrored text, ...).
846 tools::Long nS0
=nStartAngle
;
847 tools::Long nE0
=nEndAngle
;
849 // the RectObj already mirrors at VMirror because of a 180deg rotation
850 if (! (bXMirr
&& bYMirr
)) {
851 tools::Long nTmp
=nS0
;
855 } else { // mirror contorted ellipses
856 if (bXMirr
!=bYMirr
) {
860 tools::Long nTmp
=nS0
;
865 tools::Long nTmp
=nS0
;
869 nS0
-=aGeo
.nRotationAngle
;
870 nE0
-=aGeo
.nRotationAngle
;
873 tools::Long nAngleDif
=nE0
-nS0
;
874 nStartAngle
=NormAngle36000(nS0
);
875 nEndAngle
=NormAngle36000(nE0
);
876 if (nAngleDif
==36000) nEndAngle
+=nAngleDif
; // full circle
880 ImpSetCircInfoToAttr();
883 void SdrCircObj::NbcShear(const Point
& rRef
, tools::Long nAngle
, double tn
, bool bVShear
)
885 SdrTextObj::NbcShear(rRef
,nAngle
,tn
,bVShear
);
887 ImpSetCircInfoToAttr();
890 void SdrCircObj::NbcMirror(const Point
& rRef1
, const Point
& rRef2
)
892 bool bFreeMirr
=meCircleKind
!=SdrCircKind::Full
;
895 if (bFreeMirr
) { // some preparations for using an arbitrary axis of reflection
896 Point
aCenter(maRect
.Center());
897 tools::Long nWdt
=maRect
.GetWidth()-1;
898 tools::Long nHgt
=maRect
.GetHeight()-1;
899 tools::Long nMaxRad
=(std::max(nWdt
,nHgt
)+1) /2;
902 a
= nStartAngle
* F_PI18000
;
903 aTmpPt1
=Point(FRound(cos(a
)*nMaxRad
),-FRound(sin(a
)*nMaxRad
));
904 if (nWdt
==0) aTmpPt1
.setX(0 );
905 if (nHgt
==0) aTmpPt1
.setY(0 );
908 a
= nEndAngle
* F_PI18000
;
909 aTmpPt2
=Point(FRound(cos(a
)*nMaxRad
),-FRound(sin(a
)*nMaxRad
));
910 if (nWdt
==0) aTmpPt2
.setX(0 );
911 if (nHgt
==0) aTmpPt2
.setY(0 );
913 if (aGeo
.nRotationAngle
!=0) {
914 RotatePoint(aTmpPt1
,maRect
.TopLeft(),aGeo
.nSin
,aGeo
.nCos
);
915 RotatePoint(aTmpPt2
,maRect
.TopLeft(),aGeo
.nSin
,aGeo
.nCos
);
917 if (aGeo
.nShearAngle
!=0) {
918 ShearPoint(aTmpPt1
,maRect
.TopLeft(),aGeo
.nTan
);
919 ShearPoint(aTmpPt2
,maRect
.TopLeft(),aGeo
.nTan
);
922 SdrTextObj::NbcMirror(rRef1
,rRef2
);
923 if (meCircleKind
!=SdrCircKind::Full
) { // adapt starting and finishing angle
924 MirrorPoint(aTmpPt1
,rRef1
,rRef2
);
925 MirrorPoint(aTmpPt2
,rRef1
,rRef2
);
927 if (aGeo
.nRotationAngle
!=0) {
928 RotatePoint(aTmpPt1
,maRect
.TopLeft(),-aGeo
.nSin
,aGeo
.nCos
); // -sin for reversion
929 RotatePoint(aTmpPt2
,maRect
.TopLeft(),-aGeo
.nSin
,aGeo
.nCos
); // -sin for reversion
932 if (aGeo
.nShearAngle
!=0) {
933 ShearPoint(aTmpPt1
,maRect
.TopLeft(),-aGeo
.nTan
); // -tan for reversion
934 ShearPoint(aTmpPt2
,maRect
.TopLeft(),-aGeo
.nTan
); // -tan for reversion
936 Point
aCenter(maRect
.Center());
939 // because it's mirrored, the angles are swapped, too
940 nStartAngle
=GetAngle(aTmpPt2
);
941 nEndAngle
=GetAngle(aTmpPt1
);
942 tools::Long nAngleDif
=nEndAngle
-nStartAngle
;
943 nStartAngle
=NormAngle36000(nStartAngle
);
944 nEndAngle
=NormAngle36000(nEndAngle
);
945 if (nAngleDif
==36000) nEndAngle
+=nAngleDif
; // full circle
948 ImpSetCircInfoToAttr();
951 SdrObjGeoData
* SdrCircObj::NewGeoData() const
953 return new SdrCircObjGeoData
;
956 void SdrCircObj::SaveGeoData(SdrObjGeoData
& rGeo
) const
958 SdrRectObj::SaveGeoData(rGeo
);
959 SdrCircObjGeoData
& rCGeo
=static_cast<SdrCircObjGeoData
&>(rGeo
);
960 rCGeo
.nStartAngle
=nStartAngle
;
961 rCGeo
.nEndAngle
=nEndAngle
;
964 void SdrCircObj::RestGeoData(const SdrObjGeoData
& rGeo
)
966 SdrRectObj::RestGeoData(rGeo
);
967 const SdrCircObjGeoData
& rCGeo
=static_cast<const SdrCircObjGeoData
&>(rGeo
);
968 nStartAngle
=rCGeo
.nStartAngle
;
969 nEndAngle
=rCGeo
.nEndAngle
;
971 ImpSetCircInfoToAttr();
974 static void Union(tools::Rectangle
& rR
, const Point
& rP
)
976 if (rP
.X()<rR
.Left ()) rR
.SetLeft(rP
.X() );
977 if (rP
.X()>rR
.Right ()) rR
.SetRight(rP
.X() );
978 if (rP
.Y()<rR
.Top ()) rR
.SetTop(rP
.Y() );
979 if (rP
.Y()>rR
.Bottom()) rR
.SetBottom(rP
.Y() );
982 void SdrCircObj::TakeUnrotatedSnapRect(tools::Rectangle
& rRect
) const
985 if (meCircleKind
!=SdrCircKind::Full
) {
986 const Point
aPntStart(GetAnglePnt(maRect
,nStartAngle
));
987 const Point
aPntEnd(GetAnglePnt(maRect
,nEndAngle
));
988 tools::Long a
=nStartAngle
;
989 tools::Long e
=nEndAngle
;
990 rRect
.SetLeft(maRect
.Right() );
991 rRect
.SetRight(maRect
.Left() );
992 rRect
.SetTop(maRect
.Bottom() );
993 rRect
.SetBottom(maRect
.Top() );
994 Union(rRect
,aPntStart
);
995 Union(rRect
,aPntEnd
);
996 if ((a
<=18000 && e
>=18000) || (a
>e
&& (a
<=18000 || e
>=18000))) {
997 Union(rRect
,maRect
.LeftCenter());
999 if ((a
<=27000 && e
>=27000) || (a
>e
&& (a
<=27000 || e
>=27000))) {
1000 Union(rRect
,maRect
.BottomCenter());
1003 Union(rRect
,maRect
.RightCenter());
1005 if ((a
<=9000 && e
>=9000) || (a
>e
&& (a
<=9000 || e
>=9000))) {
1006 Union(rRect
,maRect
.TopCenter());
1008 if (meCircleKind
==SdrCircKind::Section
) {
1009 Union(rRect
,maRect
.Center());
1011 if (aGeo
.nRotationAngle
!=0) {
1012 Point
aDst(rRect
.TopLeft());
1013 aDst
-=maRect
.TopLeft();
1015 RotatePoint(aDst
,Point(),aGeo
.nSin
,aGeo
.nCos
);
1017 rRect
.Move(aDst
.X(),aDst
.Y());
1020 if (aGeo
.nShearAngle
==0)
1023 tools::Long nDst
=FRound((rRect
.Bottom()-rRect
.Top())*aGeo
.nTan
);
1024 if (aGeo
.nShearAngle
>0) {
1025 Point
aRef(rRect
.TopLeft());
1026 rRect
.AdjustLeft( -nDst
);
1027 Point
aTmpPt(rRect
.TopLeft());
1028 RotatePoint(aTmpPt
,aRef
,aGeo
.nSin
,aGeo
.nCos
);
1029 aTmpPt
-=rRect
.TopLeft();
1030 rRect
.Move(aTmpPt
.X(),aTmpPt
.Y());
1032 rRect
.AdjustRight( -nDst
);
1036 void SdrCircObj::RecalcSnapRect()
1038 if (PaintNeedsXPolyCirc()) {
1039 maSnapRect
=GetXPoly().GetBoundRect();
1041 TakeUnrotatedSnapRect(maSnapRect
);
1045 void SdrCircObj::NbcSetSnapRect(const tools::Rectangle
& rRect
)
1047 if (aGeo
.nRotationAngle
!=0 || aGeo
.nShearAngle
!=0 || meCircleKind
!=SdrCircKind::Full
) {
1048 tools::Rectangle
aSR0(GetSnapRect());
1049 tools::Long nWdt0
=aSR0
.Right()-aSR0
.Left();
1050 tools::Long nHgt0
=aSR0
.Bottom()-aSR0
.Top();
1051 tools::Long nWdt1
=rRect
.Right()-rRect
.Left();
1052 tools::Long nHgt1
=rRect
.Bottom()-rRect
.Top();
1053 NbcResize(maSnapRect
.TopLeft(),Fraction(nWdt1
,nWdt0
),Fraction(nHgt1
,nHgt0
));
1054 NbcMove(Size(rRect
.Left()-aSR0
.Left(),rRect
.Top()-aSR0
.Top()));
1057 ImpJustifyRect(maRect
);
1061 ImpSetCircInfoToAttr();
1064 sal_uInt32
SdrCircObj::GetSnapPointCount() const
1066 if (meCircleKind
==SdrCircKind::Full
) {
1073 Point
SdrCircObj::GetSnapPoint(sal_uInt32 i
) const
1076 case 1 : return GetAnglePnt(maRect
,nStartAngle
);
1077 case 2 : return GetAnglePnt(maRect
,nEndAngle
);
1078 default: return maRect
.Center();
1082 void SdrCircObj::Notify(SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
1085 SdrRectObj::Notify(rBC
,rHint
);
1086 ImpSetAttrToCircInfo();
1090 void SdrCircObj::ImpSetAttrToCircInfo()
1092 const SfxItemSet
& rSet
= GetObjectItemSet();
1093 SdrCircKind eNewKind
= rSet
.Get(SDRATTR_CIRCKIND
).GetValue();
1095 sal_Int32 nNewStart
= rSet
.Get(SDRATTR_CIRCSTARTANGLE
).GetValue();
1096 sal_Int32 nNewEnd
= rSet
.Get(SDRATTR_CIRCENDANGLE
).GetValue();
1098 bool bKindChg
= meCircleKind
!= eNewKind
;
1099 bool bAngleChg
= nNewStart
!= nStartAngle
|| nNewEnd
!= nEndAngle
;
1101 if(bKindChg
|| bAngleChg
)
1103 meCircleKind
= eNewKind
;
1104 nStartAngle
= nNewStart
;
1105 nEndAngle
= nNewEnd
;
1107 if(bKindChg
|| (meCircleKind
!= SdrCircKind::Full
&& bAngleChg
))
1115 void SdrCircObj::ImpSetCircInfoToAttr()
1117 const SfxItemSet
& rSet
= GetObjectItemSet();
1119 SdrCircKind eOldKindA
= rSet
.Get(SDRATTR_CIRCKIND
).GetValue();
1120 sal_Int32 nOldStartAngle
= rSet
.Get(SDRATTR_CIRCSTARTANGLE
).GetValue();
1121 sal_Int32 nOldEndAngle
= rSet
.Get(SDRATTR_CIRCENDANGLE
).GetValue();
1123 if(meCircleKind
== eOldKindA
&& nStartAngle
== nOldStartAngle
&& nEndAngle
== nOldEndAngle
)
1126 // since SetItem() implicitly calls ImpSetAttrToCircInfo()
1127 // setting the item directly is necessary here.
1128 if(meCircleKind
!= eOldKindA
)
1130 GetProperties().SetObjectItemDirect(SdrCircKindItem(meCircleKind
));
1133 if(nStartAngle
!= nOldStartAngle
)
1135 GetProperties().SetObjectItemDirect(makeSdrCircStartAngleItem(nStartAngle
));
1138 if(nEndAngle
!= nOldEndAngle
)
1140 GetProperties().SetObjectItemDirect(makeSdrCircEndAngleItem(nEndAngle
));
1144 ImpSetAttrToCircInfo();
1147 SdrObjectUniquePtr
SdrCircObj::DoConvertToPolyObj(bool bBezier
, bool bAddText
) const
1149 const bool bFill(meCircleKind
!= SdrCircKind::Arc
);
1150 const basegfx::B2DPolygon
aCircPolygon(ImpCalcXPolyCirc(meCircleKind
, maRect
, nStartAngle
, nEndAngle
));
1151 SdrObjectUniquePtr pRet
= ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon
), bFill
, bBezier
);
1155 pRet
= ImpConvertAddText(std::move(pRet
), bBezier
);
1161 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */