tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / svx / source / svdraw / svdocirc.cxx
bloba42d8c5c33d73eecbf34c445067096b71b5cc43d
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, Degree100 nAngle)
55 Point aCenter(rR.Center());
56 tools::Long nWdt=rR.Right()-rR.Left();
57 tools::Long nHgt=rR.Bottom()-rR.Top();
58 tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2;
59 double a = toRadians(nAngle);
60 Point aRetval(basegfx::fround<tools::Long>(cos(a) * nMaxRad),
61 basegfx::fround<tools::Long>(-sin(a) * nMaxRad));
62 if (nWdt==0) aRetval.setX(0 );
63 if (nHgt==0) aRetval.setY(0 );
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 SdrObjKind::CircleOrEllipse: return SdrCircKind::Full;
110 case SdrObjKind::CircleSection: return SdrCircKind::Section;
111 case SdrObjKind::CircleArc: return SdrCircKind::Arc;
112 case SdrObjKind::CircleCut: return SdrCircKind::Cut;
113 default: assert(false);
115 return SdrCircKind::Full;
118 SdrCircObj::SdrCircObj(
119 SdrModel& rSdrModel,
120 SdrCircKind eNewKind)
121 : SdrRectObj(rSdrModel)
123 nStartAngle=0_deg100;
124 nEndAngle=36000_deg100;
125 meCircleKind=eNewKind;
126 m_bClosedObj=eNewKind!=SdrCircKind::Arc;
129 SdrCircObj::SdrCircObj(SdrModel& rSdrModel, SdrCircObj const & rSource)
130 : SdrRectObj(rSdrModel, rSource)
132 meCircleKind = rSource.meCircleKind;
133 nStartAngle = rSource.nStartAngle;
134 nEndAngle = rSource.nEndAngle;
135 m_bClosedObj = rSource.m_bClosedObj;
138 SdrCircObj::SdrCircObj(
139 SdrModel& rSdrModel,
140 SdrCircKind eNewKind,
141 const tools::Rectangle& rRect)
142 : SdrRectObj(rSdrModel, rRect)
144 nStartAngle=0_deg100;
145 nEndAngle=36000_deg100;
146 meCircleKind=eNewKind;
147 m_bClosedObj=eNewKind!=SdrCircKind::Arc;
150 SdrCircObj::SdrCircObj(
151 SdrModel& rSdrModel,
152 SdrCircKind eNewKind,
153 const tools::Rectangle& rRect,
154 Degree100 nNewStartAngle,
155 Degree100 nNewEndAngle)
156 : SdrRectObj(rSdrModel, rRect)
158 Degree100 nAngleDif=nNewEndAngle-nNewStartAngle;
159 nStartAngle=NormAngle36000(nNewStartAngle);
160 nEndAngle=NormAngle36000(nNewEndAngle);
161 if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
162 meCircleKind=eNewKind;
163 m_bClosedObj=eNewKind!=SdrCircKind::Arc;
166 SdrCircObj::~SdrCircObj()
170 void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
172 bool bCanConv=!HasText() || ImpCanConvTextToCurve();
173 rInfo.bEdgeRadiusAllowed = false;
174 rInfo.bCanConvToPath=bCanConv;
175 rInfo.bCanConvToPoly=bCanConv;
176 rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
179 SdrObjKind SdrCircObj::GetObjIdentifier() const
181 switch (meCircleKind)
183 case SdrCircKind::Full: return SdrObjKind::CircleOrEllipse;
184 case SdrCircKind::Section: return SdrObjKind::CircleSection;
185 case SdrCircKind::Cut: return SdrObjKind::CircleCut;
186 case SdrCircKind::Arc: return SdrObjKind::CircleArc;
187 default: assert(false);
189 return SdrObjKind::CircleOrEllipse;
192 bool SdrCircObj::PaintNeedsXPolyCirc() const
194 // XPoly is necessary for all rotated ellipse objects, circle and
195 // ellipse segments.
196 // If not WIN, then (for now) also for circle/ellipse segments and circle/
197 // ellipse arcs (for precision)
198 bool bNeed = maGeo.m_nRotationAngle || maGeo.m_nShearAngle || meCircleKind == SdrCircKind::Cut;
199 // If not WIN, then for everything except full circle (for now!)
200 if (meCircleKind!=SdrCircKind::Full) bNeed = true;
202 const SfxItemSet& rSet = GetObjectItemSet();
203 if(!bNeed)
205 // XPoly is necessary for everything that isn't LineSolid or LineNone
206 drawing::LineStyle eLine = rSet.Get(XATTR_LINESTYLE).GetValue();
207 bNeed = eLine != drawing::LineStyle_NONE && eLine != drawing::LineStyle_SOLID;
209 // XPoly is necessary for thick lines
210 if(!bNeed && eLine != drawing::LineStyle_NONE)
211 bNeed = rSet.Get(XATTR_LINEWIDTH).GetValue() != 0;
213 // XPoly is necessary for circle arcs with line ends
214 if(!bNeed && meCircleKind == SdrCircKind::Arc)
216 // start of the line is here if StartPolygon, StartWidth!=0
217 bNeed=rSet.Get(XATTR_LINESTART).GetLineStartValue().count() != 0 &&
218 rSet.Get(XATTR_LINESTARTWIDTH).GetValue() != 0;
220 if(!bNeed)
222 // end of the line is here if EndPolygon, EndWidth!=0
223 bNeed = rSet.Get(XATTR_LINEEND).GetLineEndValue().count() != 0 &&
224 rSet.Get(XATTR_LINEENDWIDTH).GetValue() != 0;
229 // XPoly is necessary if Fill !=None and !=Solid
230 if(!bNeed && meCircleKind != SdrCircKind::Arc)
232 drawing::FillStyle eFill=rSet.Get(XATTR_FILLSTYLE).GetValue();
233 bNeed = eFill != drawing::FillStyle_NONE && eFill != drawing::FillStyle_SOLID;
236 if(!bNeed && meCircleKind != SdrCircKind::Full && nStartAngle == nEndAngle)
237 bNeed = true; // otherwise we're drawing a full circle
239 return bNeed;
242 basegfx::B2DPolygon SdrCircObj::ImpCalcXPolyCirc(const SdrCircKind eCircleKind, const tools::Rectangle& rRect1, Degree100 nStart, Degree100 nEnd) const
244 const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRect1);
245 basegfx::B2DPolygon aCircPolygon;
247 if(SdrCircKind::Full == eCircleKind)
249 // create full circle. Do not use createPolygonFromEllipse; it's necessary
250 // to get the start point to the bottom of the circle to keep compatible to
251 // old geometry creation
252 aCircPolygon = basegfx::utils::createPolygonFromUnitCircle(1);
254 // needs own scaling and translation from unit circle to target size (same as
255 // would be in createPolygonFromEllipse)
256 const basegfx::B2DPoint aCenter(aRange.getCenter());
257 const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
258 aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
259 aCenter.getX(), aCenter.getY()));
260 aCircPolygon.transform(aMatrix);
262 else
264 // mirror start, end for geometry creation since model coordinate system is mirrored in Y
265 const double fStart(toRadians((36000_deg100 - nEnd) % 36000_deg100));
266 const double fEnd(toRadians((36000_deg100 - nStart) % 36000_deg100));
268 // create circle segment. This is not closed by default
269 aCircPolygon = basegfx::utils::createPolygonFromEllipseSegment(
270 aRange.getCenter(), aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
271 fStart, fEnd);
273 // check closing states
274 const bool bCloseSegment(SdrCircKind::Arc != eCircleKind);
275 const bool bCloseUsingCenter(SdrCircKind::Section == eCircleKind);
277 if(bCloseSegment)
279 if(bCloseUsingCenter)
281 // add center point at start (for historical reasons)
282 basegfx::B2DPolygon aSector;
283 aSector.append(aRange.getCenter());
284 aSector.append(aCircPolygon);
285 aCircPolygon = std::move(aSector);
288 // close
289 aCircPolygon.setClosed(true);
293 // #i76950#
294 if (maGeo.m_nShearAngle || maGeo.m_nRotationAngle)
296 // translate top left to (0,0)
297 const basegfx::B2DPoint aTopLeft(aRange.getMinimum());
298 basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
299 -aTopLeft.getX(), -aTopLeft.getY()));
301 // shear, rotate and back to top left (if needed)
302 aMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
303 -maGeo.mfTanShearAngle,
304 maGeo.m_nRotationAngle ? toRadians(36000_deg100 - maGeo.m_nRotationAngle) : 0.0,
305 aTopLeft) * aMatrix;
307 // apply transformation
308 aCircPolygon.transform(aMatrix);
311 return aCircPolygon;
314 void SdrCircObj::RecalcXPoly()
316 basegfx::B2DPolygon aPolyCirc(ImpCalcXPolyCirc(meCircleKind, getRectangle(), nStartAngle, nEndAngle));
317 mpXPoly = XPolygon(aPolyCirc);
320 OUString SdrCircObj::TakeObjNameSingul() const
322 TranslateId pID=STR_ObjNameSingulCIRC;
323 if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo.m_nShearAngle == 0_deg100)
325 switch (meCircleKind) {
326 case SdrCircKind::Full: pID=STR_ObjNameSingulCIRC; break;
327 case SdrCircKind::Section: pID=STR_ObjNameSingulSECT; break;
328 case SdrCircKind::Arc: pID=STR_ObjNameSingulCARC; break;
329 case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUT; break;
330 default: break;
332 } else {
333 switch (meCircleKind) {
334 case SdrCircKind::Full: pID=STR_ObjNameSingulCIRCE; break;
335 case SdrCircKind::Section: pID=STR_ObjNameSingulSECTE; break;
336 case SdrCircKind::Arc: pID=STR_ObjNameSingulCARCE; break;
337 case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUTE; break;
338 default: break;
341 OUString sName(SvxResId(pID));
343 OUString aName(GetName());
344 if (!aName.isEmpty())
345 sName += " '" + aName + "'";
346 return sName;
349 OUString SdrCircObj::TakeObjNamePlural() const
351 TranslateId pID=STR_ObjNamePluralCIRC;
352 if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo.m_nShearAngle == 0_deg100)
354 switch (meCircleKind) {
355 case SdrCircKind::Full: pID=STR_ObjNamePluralCIRC; break;
356 case SdrCircKind::Section: pID=STR_ObjNamePluralSECT; break;
357 case SdrCircKind::Arc: pID=STR_ObjNamePluralCARC; break;
358 case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUT; break;
359 default: break;
361 } else {
362 switch (meCircleKind) {
363 case SdrCircKind::Full: pID=STR_ObjNamePluralCIRCE; break;
364 case SdrCircKind::Section: pID=STR_ObjNamePluralSECTE; break;
365 case SdrCircKind::Arc: pID=STR_ObjNamePluralCARCE; break;
366 case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUTE; break;
367 default: break;
370 return SvxResId(pID);
373 rtl::Reference<SdrObject> SdrCircObj::CloneSdrObject(SdrModel& rTargetModel) const
375 return new SdrCircObj(rTargetModel, *this);
378 basegfx::B2DPolyPolygon SdrCircObj::TakeXorPoly() const
380 const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, getRectangle(), nStartAngle, nEndAngle));
381 return basegfx::B2DPolyPolygon(aCircPolygon);
384 namespace {
386 struct ImpCircUser : public SdrDragStatUserData
388 tools::Rectangle aR;
389 Point aCenter;
390 Point aP1;
391 tools::Long nHgt;
392 tools::Long nWdt;
393 Degree100 nStart;
394 Degree100 nEnd;
396 public:
397 ImpCircUser()
398 : nHgt(0),
399 nWdt(0),
400 nStart(0),
401 nEnd(0)
403 void SetCreateParams(SdrDragStat const & rStat);
408 sal_uInt32 SdrCircObj::GetHdlCount() const
410 if(SdrCircKind::Full != meCircleKind)
412 return 10;
414 else
416 return 8;
420 void SdrCircObj::AddToHdlList(SdrHdlList& rHdlList) const
422 for (sal_uInt32 nHdlNum=(SdrCircKind::Full==meCircleKind)?2:0; nHdlNum<=9; ++nHdlNum)
424 Point aPnt;
425 SdrHdlKind eLocalKind(SdrHdlKind::Move);
426 sal_uInt32 nPNum(0);
427 tools::Rectangle aRectangle = getRectangle();
428 switch (nHdlNum)
430 case 0:
431 aPnt = GetAnglePnt(aRectangle, nStartAngle);
432 eLocalKind = SdrHdlKind::Circle;
433 nPNum = 1;
434 break;
435 case 1:
436 aPnt = GetAnglePnt(aRectangle, nEndAngle);
437 eLocalKind = SdrHdlKind::Circle;
438 nPNum = 2;
439 break;
440 case 2:
441 aPnt = aRectangle.TopLeft();
442 eLocalKind = SdrHdlKind::UpperLeft;
443 break;
444 case 3:
445 aPnt = aRectangle.TopCenter();
446 eLocalKind = SdrHdlKind::Upper;
447 break;
448 case 4:
449 aPnt = aRectangle.TopRight();
450 eLocalKind = SdrHdlKind::UpperRight;
451 break;
452 case 5:
453 aPnt = aRectangle.LeftCenter();
454 eLocalKind = SdrHdlKind::Left;
455 break;
456 case 6:
457 aPnt = aRectangle.RightCenter();
458 eLocalKind = SdrHdlKind::Right;
459 break;
460 case 7:
461 aPnt = aRectangle.BottomLeft();
462 eLocalKind = SdrHdlKind::LowerLeft;
463 break;
464 case 8:
465 aPnt = aRectangle.BottomCenter();
466 eLocalKind = SdrHdlKind::Lower;
467 break;
468 case 9:
469 aPnt = aRectangle.BottomRight();
470 eLocalKind = SdrHdlKind::LowerRight;
471 break;
474 if (maGeo.m_nShearAngle)
476 ShearPoint(aPnt, aRectangle.TopLeft(), maGeo.mfTanShearAngle);
479 if (maGeo.m_nRotationAngle)
481 RotatePoint(aPnt, aRectangle.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
484 std::unique_ptr<SdrHdl> pH(new SdrHdl(aPnt,eLocalKind));
485 pH->SetPointNum(nPNum);
486 pH->SetObj(const_cast<SdrCircObj*>(this));
487 pH->SetRotationAngle(maGeo.m_nRotationAngle);
488 rHdlList.AddHdl(std::move(pH));
493 bool SdrCircObj::hasSpecialDrag() const
495 return true;
498 bool SdrCircObj::beginSpecialDrag(SdrDragStat& rDrag) const
500 const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
502 if(bAngle)
504 if(1 == rDrag.GetHdl()->GetPointNum() || 2 == rDrag.GetHdl()->GetPointNum())
506 rDrag.SetNoSnap();
509 return true;
512 return SdrTextObj::beginSpecialDrag(rDrag);
515 bool SdrCircObj::applySpecialDrag(SdrDragStat& rDrag)
517 const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
519 if(bAngle)
521 Point aPt(rDrag.GetNow());
523 if (maGeo.m_nRotationAngle)
524 RotatePoint(aPt, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
526 if (maGeo.m_nShearAngle)
527 ShearPoint(aPt, getRectangle().TopLeft(), -maGeo.mfTanShearAngle);
529 aPt -= getRectangle().Center();
531 tools::Long nWdt = getRectangle().Right() - getRectangle().Left();
532 tools::Long nHgt = getRectangle().Bottom() - getRectangle().Top();
534 if(nWdt>=nHgt)
536 aPt.setY(BigMulDiv(aPt.Y(),nWdt,nHgt) );
538 else
540 aPt.setX(BigMulDiv(aPt.X(),nHgt,nWdt) );
543 Degree100 nAngle=NormAngle36000(GetAngle(aPt));
545 if (rDrag.GetView() && rDrag.GetView()->IsAngleSnapEnabled())
547 Degree100 nSA=rDrag.GetView()->GetSnapAngle();
549 if (nSA)
551 nAngle+=nSA/2_deg100;
552 nAngle/=nSA;
553 nAngle*=nSA;
554 nAngle=NormAngle36000(nAngle);
558 if(1 == rDrag.GetHdl()->GetPointNum())
560 nStartAngle = nAngle;
562 else if(2 == rDrag.GetHdl()->GetPointNum())
564 nEndAngle = nAngle;
567 SetBoundAndSnapRectsDirty();
568 SetXPolyDirty();
569 ImpSetCircInfoToAttr();
570 SetChanged();
572 return true;
574 else
576 return SdrTextObj::applySpecialDrag(rDrag);
580 OUString SdrCircObj::getSpecialDragComment(const SdrDragStat& rDrag) const
582 const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
584 if(bCreateComment)
586 OUStringBuffer aBuf(ImpGetDescriptionStr(STR_ViewCreateObj));
587 const sal_uInt32 nPointCount(rDrag.GetPointCount());
589 if(SdrCircKind::Full != meCircleKind && nPointCount > 2)
591 const ImpCircUser* pU = static_cast<const ImpCircUser*>(rDrag.GetUser());
592 Degree100 nAngle;
594 aBuf.append(" (");
596 if(3 == nPointCount)
598 nAngle = pU->nStart;
600 else
602 nAngle = pU->nEnd;
605 aBuf.append(SdrModel::GetAngleString(nAngle));
606 aBuf.append(')');
609 return aBuf.makeStringAndClear();
611 else
613 const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
615 if(bAngle)
617 const Degree100 nAngle(1 == rDrag.GetHdl()->GetPointNum() ? nStartAngle : nEndAngle);
619 return ImpGetDescriptionStr(STR_DragCircAngle) +
620 " (" +
621 SdrModel::GetAngleString(nAngle) +
622 ")";
624 else
626 return SdrTextObj::getSpecialDragComment(rDrag);
632 void ImpCircUser::SetCreateParams(SdrDragStat const & rStat)
634 rStat.TakeCreateRect(aR);
635 aR.Normalize();
636 aCenter=aR.Center();
637 nWdt=aR.Right()-aR.Left();
638 nHgt=aR.Bottom()-aR.Top();
639 nStart=0_deg100;
640 nEnd=36000_deg100;
641 if (rStat.GetPointCount()>2) {
642 Point aP(rStat.GetPoint(2)-aCenter);
643 if (nWdt==0) aP.setX(0 );
644 if (nHgt==0) aP.setY(0 );
645 if (nWdt>=nHgt) {
646 if (nHgt!=0) aP.setY(aP.Y()*nWdt/nHgt );
647 } else {
648 if (nWdt!=0) aP.setX(aP.X()*nHgt/nWdt );
650 nStart=NormAngle36000(GetAngle(aP));
651 if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) {
652 Degree100 nSA=rStat.GetView()->GetSnapAngle();
653 if (nSA) { // angle snapping
654 nStart+=nSA/2_deg100;
655 nStart/=nSA;
656 nStart*=nSA;
657 nStart=NormAngle36000(nStart);
660 aP1 = GetAnglePnt(aR,nStart);
661 nEnd=nStart;
662 } else aP1=aCenter;
663 if (rStat.GetPointCount()<=3)
664 return;
666 Point aP(rStat.GetPoint(3)-aCenter);
667 if (nWdt>=nHgt) {
668 aP.setY(BigMulDiv(aP.Y(),nWdt,nHgt) );
669 } else {
670 aP.setX(BigMulDiv(aP.X(),nHgt,nWdt) );
672 nEnd=NormAngle36000(GetAngle(aP));
673 if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) {
674 Degree100 nSA=rStat.GetView()->GetSnapAngle();
675 if (nSA) { // angle snapping
676 nEnd+=nSA/2_deg100;
677 nEnd/=nSA;
678 nEnd*=nSA;
679 nEnd=NormAngle36000(nEnd);
684 void SdrCircObj::ImpSetCreateParams(SdrDragStat& rStat)
686 ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
687 if (pU==nullptr) {
688 pU=new ImpCircUser;
689 rStat.SetUser(std::unique_ptr<ImpCircUser>(pU));
691 pU->SetCreateParams(rStat);
694 bool SdrCircObj::BegCreate(SdrDragStat& rStat)
696 rStat.SetOrtho4Possible();
697 tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
698 aRect1.Normalize();
699 rStat.SetActionRect(aRect1);
700 setRectangle(aRect1);
701 ImpSetCreateParams(rStat);
702 return true;
705 bool SdrCircObj::MovCreate(SdrDragStat& rStat)
707 ImpSetCreateParams(rStat);
708 ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
709 rStat.SetActionRect(pU->aR);
710 setRectangle(pU->aR); // for ObjName
711 ImpJustifyRect(maRectangle);
712 nStartAngle=pU->nStart;
713 nEndAngle=pU->nEnd;
714 SetBoundRectDirty();
715 m_bSnapRectDirty=true;
716 SetXPolyDirty();
718 // #i103058# push current angle settings to ItemSet to
719 // allow FullDrag visualisation
720 if(rStat.GetPointCount() >= 4)
722 ImpSetCircInfoToAttr();
725 return true;
728 bool SdrCircObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
730 ImpSetCreateParams(rStat);
731 ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
732 bool bRet = false;
733 if (eCmd==SdrCreateCmd::ForceEnd && rStat.GetPointCount()<4) meCircleKind=SdrCircKind::Full;
734 if (meCircleKind==SdrCircKind::Full) {
735 bRet=rStat.GetPointCount()>=2;
736 if (bRet) {
737 tools::Rectangle aRectangle(pU->aR);
738 ImpJustifyRect(aRectangle);
739 setRectangle(aRectangle);
741 } else {
742 rStat.SetNoSnap(rStat.GetPointCount()>=2);
743 rStat.SetOrtho4Possible(rStat.GetPointCount()<2);
744 bRet=rStat.GetPointCount()>=4;
745 if (bRet) {
746 tools::Rectangle aRectangle(pU->aR);
747 ImpJustifyRect(aRectangle);
748 setRectangle(aRectangle);
749 nStartAngle=pU->nStart;
750 nEndAngle=pU->nEnd;
753 m_bClosedObj=meCircleKind!=SdrCircKind::Arc;
754 SetBoundAndSnapRectsDirty();
755 SetXPolyDirty();
756 ImpSetCircInfoToAttr();
757 if (bRet)
758 rStat.SetUser(nullptr);
759 return bRet;
762 void SdrCircObj::BrkCreate(SdrDragStat& rStat)
764 rStat.SetUser(nullptr);
767 bool SdrCircObj::BckCreate(SdrDragStat& rStat)
769 rStat.SetNoSnap(rStat.GetPointCount()>=3);
770 rStat.SetOrtho4Possible(rStat.GetPointCount()<3);
771 return meCircleKind!=SdrCircKind::Full;
774 basegfx::B2DPolyPolygon SdrCircObj::TakeCreatePoly(const SdrDragStat& rDrag) const
776 const ImpCircUser* pU = static_cast<const ImpCircUser*>(rDrag.GetUser());
778 if(rDrag.GetPointCount() < 4)
780 // force to OBJ_CIRC to get full visualisation
781 basegfx::B2DPolyPolygon aRetval(ImpCalcXPolyCirc(SdrCircKind::Full, pU->aR, pU->nStart, pU->nEnd));
783 if(3 == rDrag.GetPointCount())
785 // add edge to first point on ellipse
786 basegfx::B2DPolygon aNew;
788 aNew.append(basegfx::B2DPoint(pU->aCenter.X(), pU->aCenter.Y()));
789 aNew.append(basegfx::B2DPoint(pU->aP1.X(), pU->aP1.Y()));
790 aRetval.append(aNew);
793 return aRetval;
795 else
797 return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind, pU->aR, pU->nStart, pU->nEnd));
801 PointerStyle SdrCircObj::GetCreatePointer() const
803 switch (meCircleKind) {
804 case SdrCircKind::Full: return PointerStyle::DrawEllipse;
805 case SdrCircKind::Section: return PointerStyle::DrawPie;
806 case SdrCircKind::Arc: return PointerStyle::DrawArc;
807 case SdrCircKind::Cut: return PointerStyle::DrawCircleCut;
808 default: break;
809 } // switch
810 return PointerStyle::Cross;
813 void SdrCircObj::NbcMove(const Size& aSize)
815 moveRectangle(aSize.Width(), aSize.Height());
816 moveOutRectangle(aSize.Width(), aSize.Height());
817 maSnapRect.Move(aSize);
818 SetXPolyDirty();
819 SetBoundAndSnapRectsDirty(true);
822 void SdrCircObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
824 Degree100 nAngle0 = maGeo.m_nRotationAngle;
825 bool bNoShearRota = (maGeo.m_nRotationAngle == 0_deg100 && maGeo.m_nShearAngle == 0_deg100);
826 SdrTextObj::NbcResize(rRef,xFact,yFact);
827 bNoShearRota |= (maGeo.m_nRotationAngle == 0_deg100 && maGeo.m_nShearAngle == 0_deg100);
828 if (meCircleKind!=SdrCircKind::Full) {
829 bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
830 bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
831 if (bXMirr || bYMirr) {
832 // At bXMirr!=bYMirr we should actually swap both line ends.
833 // That, however, is pretty bad (because of forced "hard" formatting).
834 // Alternatively, we could implement a bMirrored flag (maybe even
835 // a more general one, e. g. for mirrored text, ...).
836 Degree100 nS0=nStartAngle;
837 Degree100 nE0=nEndAngle;
838 if (bNoShearRota) {
839 // the RectObj already mirrors at VMirror because of a 180deg rotation
840 if (! (bXMirr && bYMirr)) {
841 Degree100 nTmp=nS0;
842 nS0=18000_deg100-nE0;
843 nE0=18000_deg100-nTmp;
845 } else { // mirror contorted ellipses
846 if (bXMirr!=bYMirr) {
847 nS0+=nAngle0;
848 nE0+=nAngle0;
849 if (bXMirr) {
850 Degree100 nTmp=nS0;
851 nS0=18000_deg100-nE0;
852 nE0=18000_deg100-nTmp;
854 if (bYMirr) {
855 Degree100 nTmp=nS0;
856 nS0=-nE0;
857 nE0=-nTmp;
859 nS0 -= maGeo.m_nRotationAngle;
860 nE0 -= maGeo.m_nRotationAngle;
863 Degree100 nAngleDif=nE0-nS0;
864 nStartAngle=NormAngle36000(nS0);
865 nEndAngle =NormAngle36000(nE0);
866 if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
869 SetXPolyDirty();
870 ImpSetCircInfoToAttr();
873 void SdrCircObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
875 SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
876 SetXPolyDirty();
877 ImpSetCircInfoToAttr();
880 void SdrCircObj::NbcMirror(const Point& rRef1, const Point& rRef2)
882 bool bFreeMirr=meCircleKind!=SdrCircKind::Full;
883 Point aTmpPt1;
884 Point aTmpPt2;
885 if (bFreeMirr) { // some preparations for using an arbitrary axis of reflection
886 Point aCenter(getRectangle().Center());
887 tools::Long nWdt = getRectangle().GetWidth() - 1;
888 tools::Long nHgt = getRectangle().GetHeight() - 1;
889 tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2;
890 // starting point
891 double a = toRadians(nStartAngle);
892 aTmpPt1 = Point(basegfx::fround<tools::Long>(cos(a) * nMaxRad),
893 basegfx::fround<tools::Long>(-sin(a) * nMaxRad));
894 if (nWdt==0) aTmpPt1.setX(0 );
895 if (nHgt==0) aTmpPt1.setY(0 );
896 aTmpPt1+=aCenter;
897 // finishing point
898 a = toRadians(nEndAngle);
899 aTmpPt2 = Point(basegfx::fround<tools::Long>(cos(a) * nMaxRad),
900 basegfx::fround<tools::Long>(-sin(a) * nMaxRad));
901 if (nWdt==0) aTmpPt2.setX(0 );
902 if (nHgt==0) aTmpPt2.setY(0 );
903 aTmpPt2+=aCenter;
904 if (maGeo.m_nRotationAngle)
906 RotatePoint(aTmpPt1, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
907 RotatePoint(aTmpPt2, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
909 if (maGeo.m_nShearAngle)
911 ShearPoint(aTmpPt1, getRectangle().TopLeft(), maGeo.mfTanShearAngle);
912 ShearPoint(aTmpPt2, getRectangle().TopLeft(), maGeo.mfTanShearAngle);
915 SdrTextObj::NbcMirror(rRef1,rRef2);
916 if (meCircleKind!=SdrCircKind::Full) { // adapt starting and finishing angle
917 MirrorPoint(aTmpPt1,rRef1,rRef2);
918 MirrorPoint(aTmpPt2,rRef1,rRef2);
919 // unrotate:
920 if (maGeo.m_nRotationAngle)
922 RotatePoint(aTmpPt1, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion
923 RotatePoint(aTmpPt2, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion
925 // unshear:
926 if (maGeo.m_nShearAngle)
928 ShearPoint(aTmpPt1, getRectangle().TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion
929 ShearPoint(aTmpPt2, getRectangle().TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion
931 Point aCenter(getRectangle().Center());
932 aTmpPt1-=aCenter;
933 aTmpPt2-=aCenter;
934 // because it's mirrored, the angles are swapped, too
935 nStartAngle=GetAngle(aTmpPt2);
936 nEndAngle =GetAngle(aTmpPt1);
937 Degree100 nAngleDif=nEndAngle-nStartAngle;
938 nStartAngle=NormAngle36000(nStartAngle);
939 nEndAngle =NormAngle36000(nEndAngle);
940 if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
942 SetXPolyDirty();
943 ImpSetCircInfoToAttr();
946 std::unique_ptr<SdrObjGeoData> SdrCircObj::NewGeoData() const
948 return std::make_unique<SdrCircObjGeoData>();
951 void SdrCircObj::SaveGeoData(SdrObjGeoData& rGeo) const
953 SdrRectObj::SaveGeoData(rGeo);
954 SdrCircObjGeoData& rCGeo=static_cast<SdrCircObjGeoData&>(rGeo);
955 rCGeo.nStartAngle=nStartAngle;
956 rCGeo.nEndAngle =nEndAngle;
959 void SdrCircObj::RestoreGeoData(const SdrObjGeoData& rGeo)
961 SdrRectObj::RestoreGeoData(rGeo);
962 const SdrCircObjGeoData& rCGeo=static_cast<const SdrCircObjGeoData&>(rGeo);
963 nStartAngle=rCGeo.nStartAngle;
964 nEndAngle =rCGeo.nEndAngle;
965 SetXPolyDirty();
966 ImpSetCircInfoToAttr();
969 static void Union(tools::Rectangle& rR, const Point& rP)
971 if (rP.X()<rR.Left ()) rR.SetLeft(rP.X() );
972 if (rP.X()>rR.Right ()) rR.SetRight(rP.X() );
973 if (rP.Y()<rR.Top ()) rR.SetTop(rP.Y() );
974 if (rP.Y()>rR.Bottom()) rR.SetBottom(rP.Y() );
977 void SdrCircObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
979 rRect = getRectangle();
980 if (meCircleKind!=SdrCircKind::Full) {
981 const Point aPntStart(GetAnglePnt(getRectangle(), nStartAngle));
982 const Point aPntEnd(GetAnglePnt(getRectangle(), nEndAngle));
983 Degree100 a=nStartAngle;
984 Degree100 e=nEndAngle;
985 rRect.SetLeft(getRectangle().Right() );
986 rRect.SetRight(getRectangle().Left() );
987 rRect.SetTop(getRectangle().Bottom() );
988 rRect.SetBottom(getRectangle().Top() );
989 Union(rRect,aPntStart);
990 Union(rRect,aPntEnd);
991 if ((a<=18000_deg100 && e>=18000_deg100) || (a>e && (a<=18000_deg100 || e>=18000_deg100))) {
992 Union(rRect, getRectangle().LeftCenter());
994 if ((a<=27000_deg100 && e>=27000_deg100) || (a>e && (a<=27000_deg100 || e>=27000_deg100))) {
995 Union(rRect, getRectangle().BottomCenter());
997 if (a>e) {
998 Union(rRect, getRectangle().RightCenter());
1000 if ((a<=9000_deg100 && e>=9000_deg100) || (a>e && (a<=9000_deg100 || e>=9000_deg100))) {
1001 Union(rRect, getRectangle().TopCenter());
1003 if (meCircleKind==SdrCircKind::Section) {
1004 Union(rRect, getRectangle().Center());
1006 if (maGeo.m_nRotationAngle)
1008 Point aDst(rRect.TopLeft());
1009 aDst -= getRectangle().TopLeft();
1010 Point aDst0(aDst);
1011 RotatePoint(aDst,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1012 aDst-=aDst0;
1013 rRect.Move(aDst.X(),aDst.Y());
1016 if (maGeo.m_nShearAngle==0_deg100)
1017 return;
1019 tools::Long nDst = basegfx::fround<tools::Long>((rRect.Bottom() - rRect.Top()) * maGeo.mfTanShearAngle);
1020 if (maGeo.m_nShearAngle > 0_deg100)
1022 Point aRef(rRect.TopLeft());
1023 rRect.AdjustLeft( -nDst );
1024 Point aTmpPt(rRect.TopLeft());
1025 RotatePoint(aTmpPt, aRef, maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1026 aTmpPt-=rRect.TopLeft();
1027 rRect.Move(aTmpPt.X(),aTmpPt.Y());
1028 } else {
1029 rRect.AdjustRight( -nDst );
1033 void SdrCircObj::RecalcSnapRect()
1035 if (PaintNeedsXPolyCirc()) {
1036 maSnapRect=GetXPoly().GetBoundRect();
1037 } else {
1038 TakeUnrotatedSnapRect(maSnapRect);
1042 void SdrCircObj::NbcSetSnapRect(const tools::Rectangle& rRect)
1044 if (maGeo.m_nRotationAngle || maGeo.m_nShearAngle || meCircleKind != SdrCircKind::Full)
1046 tools::Rectangle aSR0(GetSnapRect());
1047 tools::Long nWdt0=aSR0.Right()-aSR0.Left();
1048 tools::Long nHgt0=aSR0.Bottom()-aSR0.Top();
1049 tools::Long nWdt1=rRect.Right()-rRect.Left();
1050 tools::Long nHgt1=rRect.Bottom()-rRect.Top();
1051 NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
1052 NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
1053 } else {
1054 setRectangle(rRect);
1055 ImpJustifyRect(maRectangle);
1057 SetBoundAndSnapRectsDirty();
1058 SetXPolyDirty();
1059 ImpSetCircInfoToAttr();
1062 sal_uInt32 SdrCircObj::GetSnapPointCount() const
1064 if (meCircleKind==SdrCircKind::Full) {
1065 return 1;
1066 } else {
1067 return 3;
1071 Point SdrCircObj::GetSnapPoint(sal_uInt32 i) const
1073 switch (i)
1075 case 1 : return GetAnglePnt(getRectangle(), nStartAngle);
1076 case 2 : return GetAnglePnt(getRectangle(), nEndAngle);
1077 default: return getRectangle().Center();
1081 void SdrCircObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
1083 SetXPolyDirty();
1084 SdrRectObj::Notify(rBC,rHint);
1085 ImpSetAttrToCircInfo();
1089 void SdrCircObj::ImpSetAttrToCircInfo()
1091 const SfxItemSet& rSet = GetObjectItemSet();
1092 SdrCircKind eNewKind = rSet.Get(SDRATTR_CIRCKIND).GetValue();
1094 Degree100 nNewStart = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue();
1095 Degree100 nNewEnd = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue();
1097 bool bKindChg = meCircleKind != eNewKind;
1098 bool bAngleChg = nNewStart != nStartAngle || nNewEnd != nEndAngle;
1100 if(bKindChg || bAngleChg)
1102 meCircleKind = eNewKind;
1103 nStartAngle = nNewStart;
1104 nEndAngle = nNewEnd;
1106 if(bKindChg || (meCircleKind != SdrCircKind::Full && bAngleChg))
1108 SetXPolyDirty();
1109 SetBoundAndSnapRectsDirty();
1114 void SdrCircObj::ImpSetCircInfoToAttr()
1116 const SfxItemSet& rSet = GetObjectItemSet();
1118 SdrCircKind eOldKindA = rSet.Get(SDRATTR_CIRCKIND).GetValue();
1119 Degree100 nOldStartAngle = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue();
1120 Degree100 nOldEndAngle = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue();
1122 if(meCircleKind == eOldKindA && nStartAngle == nOldStartAngle && nEndAngle == nOldEndAngle)
1123 return;
1125 // since SetItem() implicitly calls ImpSetAttrToCircInfo()
1126 // setting the item directly is necessary here.
1127 if(meCircleKind != eOldKindA)
1129 GetProperties().SetObjectItemDirect(SdrCircKindItem(meCircleKind));
1132 if(nStartAngle != nOldStartAngle)
1134 GetProperties().SetObjectItemDirect(makeSdrCircStartAngleItem(nStartAngle));
1137 if(nEndAngle != nOldEndAngle)
1139 GetProperties().SetObjectItemDirect(makeSdrCircEndAngleItem(nEndAngle));
1142 SetXPolyDirty();
1143 ImpSetAttrToCircInfo();
1146 rtl::Reference<SdrObject> SdrCircObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
1148 const bool bFill(meCircleKind != SdrCircKind::Arc);
1149 const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, getRectangle(), nStartAngle, nEndAngle));
1150 rtl::Reference<SdrObject> pRet = ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon), bFill, bBezier);
1152 if(bAddText)
1154 pRet = ImpConvertAddText(std::move(pRet), bBezier);
1157 return pRet;
1160 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */