Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / svx / source / svdraw / svdocirc.cxx
blob47e03148a5fd1f97ee4741824f7e2cbfb2df5a3d
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 <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>
25 #include <math.h>
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;
59 double a;
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 );
64 if (nWdt!=nHgt) {
65 if (nWdt>nHgt) {
66 if (nWdt!=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) );
70 } else {
71 aRetval.setY(aRetval.Y()*nHgt/nWdt );
74 } else {
75 if (nHgt!=0) {
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) );
79 } else {
80 aRetval.setX(aRetval.X()*nWdt/nHgt );
85 aRetval+=aCenter;
86 return aRetval;
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)
107 switch (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(
119 SdrModel& rSdrModel,
120 SdrCircKind eNewKind)
121 : SdrRectObj(rSdrModel)
123 nStartAngle=0;
124 nEndAngle=36000;
125 meCircleKind=eNewKind;
126 bClosedObj=eNewKind!=SdrCircKind::Arc;
129 SdrCircObj::SdrCircObj(
130 SdrModel& rSdrModel,
131 SdrCircKind eNewKind,
132 const tools::Rectangle& rRect)
133 : SdrRectObj(rSdrModel, rRect)
135 nStartAngle=0;
136 nEndAngle=36000;
137 meCircleKind=eNewKind;
138 bClosedObj=eNewKind!=SdrCircKind::Arc;
141 SdrCircObj::SdrCircObj(
142 SdrModel& rSdrModel,
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);
180 return OBJ_CIRC;
183 bool SdrCircObj::PaintNeedsXPolyCirc() const
185 // XPoly is necessary for all rotated ellipse objects, circle and
186 // ellipse segments.
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();
194 if(!bNeed)
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;
211 if(!bNeed)
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
230 return bNeed;
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);
253 else
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,
263 fStart, fEnd);
265 // check closing states
266 const bool bCloseSegment(SdrCircKind::Arc != eCircleKind);
267 const bool bCloseUsingCenter(SdrCircKind::Section == eCircleKind);
269 if(bCloseSegment)
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;
280 // close
281 aCircPolygon.setClosed(true);
285 // #i76950#
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,
297 aTopLeft) * aMatrix;
299 // apply transformation
300 aCircPolygon.transform(aMatrix);
303 return aCircPolygon;
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;
322 default: break;
324 } else {
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;
330 default: break;
333 OUStringBuffer sName(SvxResId(pID));
335 OUString aName(GetName());
336 if (!aName.isEmpty())
338 sName.append(' ');
339 sName.append('\'');
340 sName.append(aName);
341 sName.append('\'');
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;
356 default: break;
358 } else {
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;
364 default: break;
367 return SvxResId(pID);
370 SdrCircObj* SdrCircObj::CloneSdrObject(SdrModel& rTargetModel) const
372 return CloneHelper< SdrCircObj >(rTargetModel);
375 SdrCircObj& SdrCircObj::operator=(const SdrCircObj& rObj)
377 if( this == &rObj )
378 return *this;
379 SdrRectObj::operator=(rObj);
381 meCircleKind = rObj.meCircleKind;
382 nStartAngle = rObj.nStartAngle;
383 nEndAngle = rObj.nEndAngle;
385 return *this;
388 basegfx::B2DPolyPolygon SdrCircObj::TakeXorPoly() const
390 const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, maRect, nStartAngle, nEndAngle));
391 return basegfx::B2DPolyPolygon(aCircPolygon);
394 namespace {
396 struct ImpCircUser : public SdrDragStatUserData
398 tools::Rectangle aR;
399 Point aCenter;
400 Point aP1;
401 tools::Long nHgt;
402 tools::Long nWdt;
403 tools::Long nStart;
404 tools::Long nEnd;
406 public:
407 ImpCircUser()
408 : nHgt(0),
409 nWdt(0),
410 nStart(0),
411 nEnd(0)
413 void SetCreateParams(SdrDragStat const & rStat);
418 sal_uInt32 SdrCircObj::GetHdlCount() const
420 if(SdrCircKind::Full != meCircleKind)
422 return 10;
424 else
426 return 8;
430 void SdrCircObj::AddToHdlList(SdrHdlList& rHdlList) const
432 for (sal_uInt32 nHdlNum=(SdrCircKind::Full==meCircleKind)?2:0; nHdlNum<=9; ++nHdlNum)
434 Point aPnt;
435 SdrHdlKind eLocalKind(SdrHdlKind::Move);
436 sal_uInt32 nPNum(0);
438 switch (nHdlNum)
440 case 0:
441 aPnt = GetAnglePnt(maRect,nStartAngle);
442 eLocalKind = SdrHdlKind::Circle;
443 nPNum = 1;
444 break;
445 case 1:
446 aPnt = GetAnglePnt(maRect,nEndAngle);
447 eLocalKind = SdrHdlKind::Circle;
448 nPNum = 2;
449 break;
450 case 2:
451 aPnt = maRect.TopLeft();
452 eLocalKind = SdrHdlKind::UpperLeft;
453 break;
454 case 3:
455 aPnt = maRect.TopCenter();
456 eLocalKind = SdrHdlKind::Upper;
457 break;
458 case 4:
459 aPnt = maRect.TopRight();
460 eLocalKind = SdrHdlKind::UpperRight;
461 break;
462 case 5:
463 aPnt = maRect.LeftCenter();
464 eLocalKind = SdrHdlKind::Left;
465 break;
466 case 6:
467 aPnt = maRect.RightCenter();
468 eLocalKind = SdrHdlKind::Right;
469 break;
470 case 7:
471 aPnt = maRect.BottomLeft();
472 eLocalKind = SdrHdlKind::LowerLeft;
473 break;
474 case 8:
475 aPnt = maRect.BottomCenter();
476 eLocalKind = SdrHdlKind::Lower;
477 break;
478 case 9:
479 aPnt = maRect.BottomRight();
480 eLocalKind = SdrHdlKind::LowerRight;
481 break;
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
505 return true;
508 bool SdrCircObj::beginSpecialDrag(SdrDragStat& rDrag) const
510 const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
512 if(bAngle)
514 if(1 == rDrag.GetHdl()->GetPointNum() || 2 == rDrag.GetHdl()->GetPointNum())
516 rDrag.SetNoSnap();
519 return true;
522 return SdrTextObj::beginSpecialDrag(rDrag);
525 bool SdrCircObj::applySpecialDrag(SdrDragStat& rDrag)
527 const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
529 if(bAngle)
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();
544 if(nWdt>=nHgt)
546 aPt.setY(BigMulDiv(aPt.Y(),nWdt,nHgt) );
548 else
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();
559 if (nSA!=0)
561 nAngle+=nSA/2;
562 nAngle/=nSA;
563 nAngle*=nSA;
564 nAngle=NormAngle36000(nAngle);
568 if(1 == rDrag.GetHdl()->GetPointNum())
570 nStartAngle = nAngle;
572 else if(2 == rDrag.GetHdl()->GetPointNum())
574 nEndAngle = nAngle;
577 SetRectsDirty();
578 SetXPolyDirty();
579 ImpSetCircInfoToAttr();
580 SetChanged();
582 return true;
584 else
586 return SdrTextObj::applySpecialDrag(rDrag);
590 OUString SdrCircObj::getSpecialDragComment(const SdrDragStat& rDrag) const
592 const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
594 if(bCreateComment)
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());
602 sal_Int32 nAngle;
604 aBuf.append(" (");
606 if(3 == nPointCount)
608 nAngle = pU->nStart;
610 else
612 nAngle = pU->nEnd;
615 aBuf.append(SdrModel::GetAngleString(nAngle));
616 aBuf.append(')');
619 return aBuf.makeStringAndClear();
621 else
623 const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
625 if(bAngle)
627 const sal_Int32 nAngle(1 == rDrag.GetHdl()->GetPointNum() ? nStartAngle : nEndAngle);
629 OUStringBuffer aBuf(ImpGetDescriptionStr(STR_DragCircAngle));
630 aBuf.append(" (");
631 aBuf.append(SdrModel::GetAngleString(nAngle));
632 aBuf.append(')');
634 return aBuf.makeStringAndClear();
636 else
638 return SdrTextObj::getSpecialDragComment(rDrag);
644 void ImpCircUser::SetCreateParams(SdrDragStat const & rStat)
646 rStat.TakeCreateRect(aR);
647 aR.Justify();
648 aCenter=aR.Center();
649 nWdt=aR.Right()-aR.Left();
650 nHgt=aR.Bottom()-aR.Top();
651 nStart=0;
652 nEnd=36000;
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 );
657 if (nWdt>=nHgt) {
658 if (nHgt!=0) aP.setY(aP.Y()*nWdt/nHgt );
659 } else {
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
666 nStart+=nSA/2;
667 nStart/=nSA;
668 nStart*=nSA;
669 nStart=NormAngle36000(nStart);
672 aP1 = GetAnglePnt(aR,nStart);
673 nEnd=nStart;
674 } else aP1=aCenter;
675 if (rStat.GetPointCount()<=3)
676 return;
678 Point aP(rStat.GetPoint(3)-aCenter);
679 if (nWdt>=nHgt) {
680 aP.setY(BigMulDiv(aP.Y(),nWdt,nHgt) );
681 } else {
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
688 nEnd+=nSA/2;
689 nEnd/=nSA;
690 nEnd*=nSA;
691 nEnd=NormAngle36000(nEnd);
696 void SdrCircObj::ImpSetCreateParams(SdrDragStat& rStat)
698 ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
699 if (pU==nullptr) {
700 pU=new ImpCircUser;
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());
710 aRect1.Justify();
711 rStat.SetActionRect(aRect1);
712 maRect = aRect1;
713 ImpSetCreateParams(rStat);
714 return true;
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;
725 nEndAngle=pU->nEnd;
726 SetBoundRectDirty();
727 bSnapRectDirty=true;
728 SetXPolyDirty();
730 // #i103058# push current angle settings to ItemSet to
731 // allow FullDrag visualisation
732 if(rStat.GetPointCount() >= 4)
734 ImpSetCircInfoToAttr();
737 return true;
740 bool SdrCircObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
742 ImpSetCreateParams(rStat);
743 ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
744 bool bRet = false;
745 if (eCmd==SdrCreateCmd::ForceEnd && rStat.GetPointCount()<4) meCircleKind=SdrCircKind::Full;
746 if (meCircleKind==SdrCircKind::Full) {
747 bRet=rStat.GetPointCount()>=2;
748 if (bRet) {
749 maRect = pU->aR;
750 ImpJustifyRect(maRect);
752 } else {
753 rStat.SetNoSnap(rStat.GetPointCount()>=2);
754 rStat.SetOrtho4Possible(rStat.GetPointCount()<2);
755 bRet=rStat.GetPointCount()>=4;
756 if (bRet) {
757 maRect = pU->aR;
758 ImpJustifyRect(maRect);
759 nStartAngle=pU->nStart;
760 nEndAngle=pU->nEnd;
763 bClosedObj=meCircleKind!=SdrCircKind::Arc;
764 SetRectsDirty();
765 SetXPolyDirty();
766 ImpSetCircInfoToAttr();
767 if (bRet)
768 rStat.SetUser(nullptr);
769 return bRet;
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);
803 return aRetval;
805 else
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;
818 default: break;
819 } // switch
820 return PointerStyle::Cross;
823 void SdrCircObj::NbcMove(const Size& aSiz)
825 maRect.Move(aSiz);
826 aOutRect.Move(aSiz);
827 maSnapRect.Move(aSiz);
828 SetXPolyDirty();
829 SetRectsDirty(true);
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;
848 if (bNoShearRota) {
849 // the RectObj already mirrors at VMirror because of a 180deg rotation
850 if (! (bXMirr && bYMirr)) {
851 tools::Long nTmp=nS0;
852 nS0=18000-nE0;
853 nE0=18000-nTmp;
855 } else { // mirror contorted ellipses
856 if (bXMirr!=bYMirr) {
857 nS0+=nAngle0;
858 nE0+=nAngle0;
859 if (bXMirr) {
860 tools::Long nTmp=nS0;
861 nS0=18000-nE0;
862 nE0=18000-nTmp;
864 if (bYMirr) {
865 tools::Long nTmp=nS0;
866 nS0=-nE0;
867 nE0=-nTmp;
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
879 SetXPolyDirty();
880 ImpSetCircInfoToAttr();
883 void SdrCircObj::NbcShear(const Point& rRef, tools::Long nAngle, double tn, bool bVShear)
885 SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
886 SetXPolyDirty();
887 ImpSetCircInfoToAttr();
890 void SdrCircObj::NbcMirror(const Point& rRef1, const Point& rRef2)
892 bool bFreeMirr=meCircleKind!=SdrCircKind::Full;
893 Point aTmpPt1;
894 Point aTmpPt2;
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;
900 double a;
901 // starting point
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 );
906 aTmpPt1+=aCenter;
907 // finishing point
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 );
912 aTmpPt2+=aCenter;
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);
926 // unrotate:
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
931 // unshear:
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());
937 aTmpPt1-=aCenter;
938 aTmpPt2-=aCenter;
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
947 SetXPolyDirty();
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;
970 SetXPolyDirty();
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
984 rRect = maRect;
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());
1002 if (a>e) {
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();
1014 Point aDst0(aDst);
1015 RotatePoint(aDst,Point(),aGeo.nSin,aGeo.nCos);
1016 aDst-=aDst0;
1017 rRect.Move(aDst.X(),aDst.Y());
1020 if (aGeo.nShearAngle==0)
1021 return;
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());
1031 } else {
1032 rRect.AdjustRight( -nDst );
1036 void SdrCircObj::RecalcSnapRect()
1038 if (PaintNeedsXPolyCirc()) {
1039 maSnapRect=GetXPoly().GetBoundRect();
1040 } else {
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()));
1055 } else {
1056 maRect=rRect;
1057 ImpJustifyRect(maRect);
1059 SetRectsDirty();
1060 SetXPolyDirty();
1061 ImpSetCircInfoToAttr();
1064 sal_uInt32 SdrCircObj::GetSnapPointCount() const
1066 if (meCircleKind==SdrCircKind::Full) {
1067 return 1;
1068 } else {
1069 return 3;
1073 Point SdrCircObj::GetSnapPoint(sal_uInt32 i) const
1075 switch (i) {
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)
1084 SetXPolyDirty();
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))
1109 SetXPolyDirty();
1110 SetRectsDirty();
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)
1124 return;
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));
1143 SetXPolyDirty();
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);
1153 if(bAddText)
1155 pRet = ImpConvertAddText(std::move(pRet), bBezier);
1158 return pRet;
1161 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */