Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / svx / source / svdraw / svdocirc.cxx
blob1cdf8be3403bbe8e00786595fd419957f50fb702
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 <editeng/eeitem.hxx>
26 #include <math.h>
27 #include <svl/style.hxx>
29 #include "svdglob.hxx"
30 #include "svx/svdstr.hrc"
32 #include <sdr/contact/viewcontactofsdrcircobj.hxx>
33 #include <sdr/properties/circleproperties.hxx>
34 #include <svx/svdattr.hxx>
35 #include <svx/svddrag.hxx>
36 #include <svx/svdetc.hxx>
37 #include <svx/svdmodel.hxx>
38 #include <svx/svdocirc.hxx>
39 #include <svx/svdopath.hxx>
40 #include <svx/svdpage.hxx>
41 #include <svx/svdpool.hxx>
42 #include <svx/svdtrans.hxx>
43 #include <svx/svdview.hxx>
44 #include <svx/sxciaitm.hxx>
45 #include <sxcikitm.hxx>
46 #include <svx/xlnedit.hxx>
47 #include <svx/xlnedwit.hxx>
48 #include <svx/xlnstit.hxx>
49 #include <svx/xlnstwit.hxx>
50 #include <svx/xlnwtit.hxx>
51 #include <svx/xpool.hxx>
55 Point GetWinkPnt(const Rectangle& rR, long nWink)
57 Point aCenter(rR.Center());
58 long nWdt=rR.Right()-rR.Left();
59 long nHgt=rR.Bottom()-rR.Top();
60 long nMaxRad=((nWdt>nHgt ? nWdt : nHgt)+1) /2;
61 double a;
62 a=nWink*nPi180;
63 Point aRetval(Round(cos(a)*nMaxRad),-Round(sin(a)*nMaxRad));
64 if (nWdt==0) aRetval.X()=0;
65 if (nHgt==0) aRetval.Y()=0;
66 if (nWdt!=nHgt) {
67 if (nWdt>nHgt) {
68 if (nWdt!=0) {
69 // stop possible overruns for very large objects
70 if (std::abs(nHgt)>32767 || std::abs(aRetval.Y())>32767) {
71 aRetval.Y()=BigMulDiv(aRetval.Y(),nHgt,nWdt);
72 } else {
73 aRetval.Y()=aRetval.Y()*nHgt/nWdt;
76 } else {
77 if (nHgt!=0) {
78 // stop possible overruns for very large objects
79 if (std::abs(nWdt)>32767 || std::abs(aRetval.X())>32767) {
80 aRetval.X()=BigMulDiv(aRetval.X(),nWdt,nHgt);
81 } else {
82 aRetval.X()=aRetval.X()*nWdt/nHgt;
87 aRetval+=aCenter;
88 return aRetval;
92 // BaseProperties section
94 sdr::properties::BaseProperties* SdrCircObj::CreateObjectSpecificProperties()
96 return new sdr::properties::CircleProperties(*this);
100 // DrawContact section
102 sdr::contact::ViewContact* SdrCircObj::CreateObjectSpecificViewContact()
104 return new sdr::contact::ViewContactOfSdrCircObj(*this);
109 TYPEINIT1(SdrCircObj,SdrRectObj);
111 SdrCircObj::SdrCircObj(SdrObjKind eNewKind)
113 nStartWink=0;
114 nEndWink=36000;
115 meCircleKind=eNewKind;
116 bClosedObj=eNewKind!=OBJ_CARC;
119 SdrCircObj::SdrCircObj(SdrObjKind eNewKind, const Rectangle& rRect):
120 SdrRectObj(rRect)
122 nStartWink=0;
123 nEndWink=36000;
124 meCircleKind=eNewKind;
125 bClosedObj=eNewKind!=OBJ_CARC;
128 SdrCircObj::SdrCircObj(SdrObjKind eNewKind, const Rectangle& rRect, long nNewStartWink, long nNewEndWink):
129 SdrRectObj(rRect)
131 long nWinkDif=nNewEndWink-nNewStartWink;
132 nStartWink=NormAngle360(nNewStartWink);
133 nEndWink=NormAngle360(nNewEndWink);
134 if (nWinkDif==36000) nEndWink+=nWinkDif; // full circle
135 meCircleKind=eNewKind;
136 bClosedObj=eNewKind!=OBJ_CARC;
139 SdrCircObj::~SdrCircObj()
143 void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
145 bool bCanConv=!HasText() || ImpCanConvTextToCurve();
146 rInfo.bEdgeRadiusAllowed = false;
147 rInfo.bCanConvToPath=bCanConv;
148 rInfo.bCanConvToPoly=bCanConv;
149 rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
152 sal_uInt16 SdrCircObj::GetObjIdentifier() const
154 return sal_uInt16(meCircleKind);
157 bool SdrCircObj::PaintNeedsXPolyCirc() const
159 // XPoly is necessary for all rotated ellipse objects, circle and
160 // ellipse segments.
161 // If not WIN, then (for now) also for circle/ellipse segments and circle/
162 // ellipse arcs (for precision)
163 bool bNeed=aGeo.nDrehWink!=0 || aGeo.nShearWink!=0 || meCircleKind==OBJ_CCUT;
164 // If not WIN, then for everything except full circle (for now!)
165 if (meCircleKind!=OBJ_CIRC) bNeed = true;
167 const SfxItemSet& rSet = GetObjectItemSet();
168 if(!bNeed)
170 // XPoly is necessary for everything that isn't LineSolid or LineNone
171 XLineStyle eLine = ((XLineStyleItem&)(rSet.Get(XATTR_LINESTYLE))).GetValue();
172 bNeed = eLine != XLINE_NONE && eLine != XLINE_SOLID;
174 // XPoly is necessary for thick lines
175 if(!bNeed && eLine != XLINE_NONE)
176 bNeed = ((XLineWidthItem&)(rSet.Get(XATTR_LINEWIDTH))).GetValue() != 0;
178 // XPoly is necessary for circle arcs with line ends
179 if(!bNeed && meCircleKind == OBJ_CARC)
181 // start of the line is here if StartPolygon, StartWidth!=0
182 bNeed=((XLineStartItem&)(rSet.Get(XATTR_LINESTART))).GetLineStartValue().count() != 0L &&
183 ((XLineStartWidthItem&)(rSet.Get(XATTR_LINESTARTWIDTH))).GetValue() != 0;
185 if(!bNeed)
187 // end of the line is here if EndPolygon, EndWidth!=0
188 bNeed = ((XLineEndItem&)(rSet.Get(XATTR_LINEEND))).GetLineEndValue().count() != 0L &&
189 ((XLineEndWidthItem&)(rSet.Get(XATTR_LINEENDWIDTH))).GetValue() != 0;
194 // XPoly is necessary if Fill !=None and !=Solid
195 if(!bNeed && meCircleKind != OBJ_CARC)
197 XFillStyle eFill=((XFillStyleItem&)(rSet.Get(XATTR_FILLSTYLE))).GetValue();
198 bNeed = eFill != XFILL_NONE && eFill != XFILL_SOLID;
201 if(!bNeed && meCircleKind != OBJ_CIRC && nStartWink == nEndWink)
202 bNeed = true; // otherwise we're drawing a full circle
204 return bNeed;
207 basegfx::B2DPolygon SdrCircObj::ImpCalcXPolyCirc(const SdrObjKind eCicrleKind, const Rectangle& rRect1, long nStart, long nEnd) const
209 const basegfx::B2DRange aRange(rRect1.Left(), rRect1.Top(), rRect1.Right(), rRect1.Bottom());
210 basegfx::B2DPolygon aCircPolygon;
212 if(OBJ_CIRC == eCicrleKind)
214 // create full circle. Do not use createPolygonFromEllipse; it's necessary
215 // to get the start point to the bottom of the circle to keep compatible to
216 // old geometry creation
217 aCircPolygon = basegfx::tools::createPolygonFromUnitCircle(1);
219 // needs own scaling and translation from unit circle to target size (same as
220 // would be in createPolygonFromEllipse)
221 const basegfx::B2DPoint aCenter(aRange.getCenter());
222 const basegfx::B2DHomMatrix aMatrix(basegfx::tools::createScaleTranslateB2DHomMatrix(
223 aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
224 aCenter.getX(), aCenter.getY()));
225 aCircPolygon.transform(aMatrix);
227 else
229 // mirror start, end for geometry creation since model coordinate system is mirrored in Y
230 // #i111715# increase numerical correctness by first dividing and not using F_PI1800
231 const double fStart((((36000 - nEnd) % 36000) / 18000.0) * F_PI);
232 const double fEnd((((36000 - nStart) % 36000) / 18000.0) * F_PI);
234 // create circle segment. This is not closed by default
235 aCircPolygon = basegfx::tools::createPolygonFromEllipseSegment(
236 aRange.getCenter(), aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
237 fStart, fEnd);
239 // check closing states
240 const bool bCloseSegment(OBJ_CARC != eCicrleKind);
241 const bool bCloseUsingCenter(OBJ_SECT == eCicrleKind);
243 if(bCloseSegment)
245 if(bCloseUsingCenter)
247 // add center point at start (for historical reasons)
248 basegfx::B2DPolygon aSector;
249 aSector.append(aRange.getCenter());
250 aSector.append(aCircPolygon);
251 aCircPolygon = aSector;
254 // close
255 aCircPolygon.setClosed(true);
259 // #i76950#
260 if(aGeo.nShearWink || aGeo.nDrehWink)
262 // translate top left to (0,0)
263 const basegfx::B2DPoint aTopLeft(aRange.getMinimum());
264 basegfx::B2DHomMatrix aMatrix(basegfx::tools::createTranslateB2DHomMatrix(
265 -aTopLeft.getX(), -aTopLeft.getY()));
267 // shear, rotate and back to top left (if needed)
268 aMatrix = basegfx::tools::createShearXRotateTranslateB2DHomMatrix(
269 aGeo.nShearWink ? tan((36000 - aGeo.nShearWink) * F_PI18000) : 0.0,
270 aGeo.nDrehWink ? (36000 - aGeo.nDrehWink) * F_PI18000 : 0.0,
271 aTopLeft) * aMatrix;
273 // apply transformation
274 aCircPolygon.transform(aMatrix);
277 return aCircPolygon;
280 void SdrCircObj::RecalcXPoly()
282 const basegfx::B2DPolygon aPolyCirc(ImpCalcXPolyCirc(meCircleKind, aRect, nStartWink, nEndWink));
283 mpXPoly = new XPolygon(aPolyCirc);
286 OUString SdrCircObj::TakeObjNameSingul() const
288 sal_uInt16 nID=STR_ObjNameSingulCIRC;
289 if (aRect.GetWidth()==aRect.GetHeight() && aGeo.nShearWink==0) {
290 switch (meCircleKind) {
291 case OBJ_CIRC: nID=STR_ObjNameSingulCIRC; break;
292 case OBJ_SECT: nID=STR_ObjNameSingulSECT; break;
293 case OBJ_CARC: nID=STR_ObjNameSingulCARC; break;
294 case OBJ_CCUT: nID=STR_ObjNameSingulCCUT; break;
295 default: break;
297 } else {
298 switch (meCircleKind) {
299 case OBJ_CIRC: nID=STR_ObjNameSingulCIRCE; break;
300 case OBJ_SECT: nID=STR_ObjNameSingulSECTE; break;
301 case OBJ_CARC: nID=STR_ObjNameSingulCARCE; break;
302 case OBJ_CCUT: nID=STR_ObjNameSingulCCUTE; break;
303 default: break;
306 OUStringBuffer sName(ImpGetResStr(nID));
308 OUString aName(GetName());
309 if (!aName.isEmpty())
311 sName.append(' ');
312 sName.append('\'');
313 sName.append(aName);
314 sName.append('\'');
316 return sName.makeStringAndClear();
319 OUString SdrCircObj::TakeObjNamePlural() const
321 sal_uInt16 nID=STR_ObjNamePluralCIRC;
322 if (aRect.GetWidth()==aRect.GetHeight() && aGeo.nShearWink==0) {
323 switch (meCircleKind) {
324 case OBJ_CIRC: nID=STR_ObjNamePluralCIRC; break;
325 case OBJ_SECT: nID=STR_ObjNamePluralSECT; break;
326 case OBJ_CARC: nID=STR_ObjNamePluralCARC; break;
327 case OBJ_CCUT: nID=STR_ObjNamePluralCCUT; break;
328 default: break;
330 } else {
331 switch (meCircleKind) {
332 case OBJ_CIRC: nID=STR_ObjNamePluralCIRCE; break;
333 case OBJ_SECT: nID=STR_ObjNamePluralSECTE; break;
334 case OBJ_CARC: nID=STR_ObjNamePluralCARCE; break;
335 case OBJ_CCUT: nID=STR_ObjNamePluralCCUTE; break;
336 default: break;
339 return ImpGetResStr(nID);
342 SdrCircObj* SdrCircObj::Clone() const
344 return CloneHelper< SdrCircObj >();
347 basegfx::B2DPolyPolygon SdrCircObj::TakeXorPoly() const
349 const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, aRect, nStartWink, nEndWink));
350 return basegfx::B2DPolyPolygon(aCircPolygon);
353 struct ImpCircUser : public SdrDragStatUserData
355 Rectangle aR;
356 Point aCenter;
357 Point aRadius;
358 Point aP1;
359 Point aP2;
360 long nMaxRad;
361 long nHgt;
362 long nWdt;
363 long nStart;
364 long nEnd;
365 long nWink;
366 bool bRight; // not yet implemented
368 public:
369 ImpCircUser()
370 : nMaxRad(0),
371 nHgt(0),
372 nWdt(0),
373 nStart(0),
374 nEnd(0),
375 nWink(0),
376 bRight(false)
378 void SetCreateParams(SdrDragStat& rStat);
381 sal_uInt32 SdrCircObj::GetHdlCount() const
383 if(OBJ_CIRC != meCircleKind)
385 return 10L;
387 else
389 return 8L;
393 SdrHdl* SdrCircObj::GetHdl(sal_uInt32 nHdlNum) const
395 if (meCircleKind==OBJ_CIRC)
397 nHdlNum += 2L;
400 SdrHdl* pH = NULL;
401 Point aPnt;
402 SdrHdlKind eLocalKind(HDL_MOVE);
403 sal_uInt32 nPNum(0);
405 switch (nHdlNum)
407 case 0:
408 aPnt = GetWinkPnt(aRect,nStartWink);
409 eLocalKind = HDL_CIRC;
410 nPNum = 1;
411 break;
412 case 1:
413 aPnt = GetWinkPnt(aRect,nEndWink);
414 eLocalKind = HDL_CIRC;
415 nPNum = 2L;
416 break;
417 case 2:
418 aPnt = aRect.TopLeft();
419 eLocalKind = HDL_UPLFT;
420 break;
421 case 3:
422 aPnt = aRect.TopCenter();
423 eLocalKind = HDL_UPPER;
424 break;
425 case 4:
426 aPnt = aRect.TopRight();
427 eLocalKind = HDL_UPRGT;
428 break;
429 case 5:
430 aPnt = aRect.LeftCenter();
431 eLocalKind = HDL_LEFT;
432 break;
433 case 6:
434 aPnt = aRect.RightCenter();
435 eLocalKind = HDL_RIGHT;
436 break;
437 case 7:
438 aPnt = aRect.BottomLeft();
439 eLocalKind = HDL_LWLFT;
440 break;
441 case 8:
442 aPnt = aRect.BottomCenter();
443 eLocalKind = HDL_LOWER;
444 break;
445 case 9:
446 aPnt = aRect.BottomRight();
447 eLocalKind = HDL_LWRGT;
448 break;
451 if (aGeo.nShearWink)
453 ShearPoint(aPnt,aRect.TopLeft(),aGeo.nTan);
456 if (aGeo.nDrehWink)
458 RotatePoint(aPnt,aRect.TopLeft(),aGeo.nSin,aGeo.nCos);
461 if (eLocalKind != HDL_MOVE)
463 pH = new SdrHdl(aPnt,eLocalKind);
464 pH->SetPointNum(nPNum);
465 pH->SetObj((SdrObject*)this);
466 pH->SetDrehWink(aGeo.nDrehWink);
469 return pH;
474 bool SdrCircObj::hasSpecialDrag() const
476 return true;
479 bool SdrCircObj::beginSpecialDrag(SdrDragStat& rDrag) const
481 const bool bWink(rDrag.GetHdl() && HDL_CIRC == rDrag.GetHdl()->GetKind());
483 if(bWink)
485 if(1 == rDrag.GetHdl()->GetPointNum() || 2 == rDrag.GetHdl()->GetPointNum())
487 rDrag.SetNoSnap(true);
490 return true;
493 return SdrTextObj::beginSpecialDrag(rDrag);
496 bool SdrCircObj::applySpecialDrag(SdrDragStat& rDrag)
498 const bool bWink(rDrag.GetHdl() && HDL_CIRC == rDrag.GetHdl()->GetKind());
500 if(bWink)
502 Point aPt(rDrag.GetNow());
504 if (aGeo.nDrehWink!=0)
505 RotatePoint(aPt,aRect.TopLeft(),-aGeo.nSin,aGeo.nCos);
507 if (aGeo.nShearWink!=0)
508 ShearPoint(aPt,aRect.TopLeft(),-aGeo.nTan);
510 aPt-=aRect.Center();
512 long nWdt=aRect.Right()-aRect.Left();
513 long nHgt=aRect.Bottom()-aRect.Top();
515 if(nWdt>=nHgt)
517 aPt.Y()=BigMulDiv(aPt.Y(),nWdt,nHgt);
519 else
521 aPt.X()=BigMulDiv(aPt.X(),nHgt,nWdt);
524 long nWink=NormAngle360(GetAngle(aPt));
526 if (rDrag.GetView() && rDrag.GetView()->IsAngleSnapEnabled())
528 long nSA=rDrag.GetView()->GetSnapAngle();
530 if (nSA!=0)
532 nWink+=nSA/2;
533 nWink/=nSA;
534 nWink*=nSA;
535 nWink=NormAngle360(nWink);
539 if(1 == rDrag.GetHdl()->GetPointNum())
541 nStartWink = nWink;
543 else if(2 == rDrag.GetHdl()->GetPointNum())
545 nEndWink = nWink;
548 SetRectsDirty();
549 SetXPolyDirty();
550 ImpSetCircInfoToAttr();
551 SetChanged();
553 return true;
555 else
557 return SdrTextObj::applySpecialDrag(rDrag);
561 OUString SdrCircObj::getSpecialDragComment(const SdrDragStat& rDrag) const
563 const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
565 if(bCreateComment)
567 OUString aStr;
568 ImpTakeDescriptionStr(STR_ViewCreateObj, aStr);
569 OUStringBuffer aBuf(aStr);
570 const sal_uInt32 nPntAnz(rDrag.GetPointAnz());
572 if(OBJ_CIRC != meCircleKind && nPntAnz > 2)
574 ImpCircUser* pU = (ImpCircUser*)rDrag.GetUser();
575 sal_Int32 nWink;
577 aBuf.appendAscii(" (");
579 if(3 == nPntAnz)
581 nWink = pU->nStart;
583 else
585 nWink = pU->nEnd;
588 aBuf.append(GetWinkStr(nWink,false));
589 aBuf.append(')');
592 return aBuf.makeStringAndClear();
594 else
596 const bool bWink(rDrag.GetHdl() && HDL_CIRC == rDrag.GetHdl()->GetKind());
598 if(bWink)
600 const sal_Int32 nWink(1 == rDrag.GetHdl()->GetPointNum() ? nStartWink : nEndWink);
602 OUString aStr;
603 ImpTakeDescriptionStr(STR_DragCircAngle, aStr);
604 OUStringBuffer aBuf(aStr);
605 aBuf.appendAscii(" (");
606 aBuf.append(GetWinkStr(nWink,false));
607 aBuf.append(')');
609 return aBuf.makeStringAndClear();
611 else
613 return SdrTextObj::getSpecialDragComment(rDrag);
620 void ImpCircUser::SetCreateParams(SdrDragStat& rStat)
622 rStat.TakeCreateRect(aR);
623 aR.Justify();
624 aCenter=aR.Center();
625 nWdt=aR.Right()-aR.Left();
626 nHgt=aR.Bottom()-aR.Top();
627 nMaxRad=((nWdt>nHgt ? nWdt : nHgt)+1) /2;
628 nStart=0;
629 nEnd=36000;
630 if (rStat.GetPointAnz()>2) {
631 Point aP(rStat.GetPoint(2)-aCenter);
632 if (nWdt==0) aP.X()=0;
633 if (nHgt==0) aP.Y()=0;
634 if (nWdt>=nHgt) {
635 if (nHgt!=0) aP.Y()=aP.Y()*nWdt/nHgt;
636 } else {
637 if (nWdt!=0) aP.X()=aP.X()*nHgt/nWdt;
639 nStart=NormAngle360(GetAngle(aP));
640 if (rStat.GetView()!=NULL && rStat.GetView()->IsAngleSnapEnabled()) {
641 long nSA=rStat.GetView()->GetSnapAngle();
642 if (nSA!=0) { // angle snapping
643 nStart+=nSA/2;
644 nStart/=nSA;
645 nStart*=nSA;
646 nStart=NormAngle360(nStart);
649 aP1 = GetWinkPnt(aR,nStart);
650 nEnd=nStart;
651 aP2=aP1;
652 } else aP1=aCenter;
653 if (rStat.GetPointAnz()>3) {
654 Point aP(rStat.GetPoint(3)-aCenter);
655 if (nWdt>=nHgt) {
656 aP.Y()=BigMulDiv(aP.Y(),nWdt,nHgt);
657 } else {
658 aP.X()=BigMulDiv(aP.X(),nHgt,nWdt);
660 nEnd=NormAngle360(GetAngle(aP));
661 if (rStat.GetView()!=NULL && rStat.GetView()->IsAngleSnapEnabled()) {
662 long nSA=rStat.GetView()->GetSnapAngle();
663 if (nSA!=0) { // angle snapping
664 nEnd+=nSA/2;
665 nEnd/=nSA;
666 nEnd*=nSA;
667 nEnd=NormAngle360(nEnd);
670 aP2 = GetWinkPnt(aR,nEnd);
671 } else aP2=aCenter;
674 void SdrCircObj::ImpSetCreateParams(SdrDragStat& rStat) const
676 ImpCircUser* pU=(ImpCircUser*)rStat.GetUser();
677 if (pU==NULL) {
678 pU=new ImpCircUser;
679 rStat.SetUser(pU);
681 pU->SetCreateParams(rStat);
684 bool SdrCircObj::BegCreate(SdrDragStat& rStat)
686 rStat.SetOrtho4Possible();
687 Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
688 aRect1.Justify();
689 rStat.SetActionRect(aRect1);
690 aRect = aRect1;
691 ImpSetCreateParams(rStat);
692 return true;
695 bool SdrCircObj::MovCreate(SdrDragStat& rStat)
697 ImpSetCreateParams(rStat);
698 ImpCircUser* pU=(ImpCircUser*)rStat.GetUser();
699 rStat.SetActionRect(pU->aR);
700 aRect=pU->aR; // for ObjName
701 ImpJustifyRect(aRect);
702 nStartWink=pU->nStart;
703 nEndWink=pU->nEnd;
704 SetBoundRectDirty();
705 bSnapRectDirty=true;
706 SetXPolyDirty();
708 // #i103058# push current angle settings to ItemSet to
709 // allow FullDrag visualisation
710 if(rStat.GetPointAnz() >= 4)
712 ImpSetCircInfoToAttr();
715 return true;
718 bool SdrCircObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
720 ImpSetCreateParams(rStat);
721 ImpCircUser* pU=(ImpCircUser*)rStat.GetUser();
722 bool bRet = false;
723 if (eCmd==SDRCREATE_FORCEEND && rStat.GetPointAnz()<4) meCircleKind=OBJ_CIRC;
724 if (meCircleKind==OBJ_CIRC) {
725 bRet=rStat.GetPointAnz()>=2;
726 if (bRet) {
727 aRect=pU->aR;
728 ImpJustifyRect(aRect);
730 } else {
731 rStat.SetNoSnap(rStat.GetPointAnz()>=2);
732 rStat.SetOrtho4Possible(rStat.GetPointAnz()<2);
733 bRet=rStat.GetPointAnz()>=4;
734 if (bRet) {
735 aRect=pU->aR;
736 ImpJustifyRect(aRect);
737 nStartWink=pU->nStart;
738 nEndWink=pU->nEnd;
741 bClosedObj=meCircleKind!=OBJ_CARC;
742 SetRectsDirty();
743 SetXPolyDirty();
744 ImpSetCircInfoToAttr();
745 if (bRet) {
746 delete pU;
747 rStat.SetUser(NULL);
749 return bRet;
752 void SdrCircObj::BrkCreate(SdrDragStat& rStat)
754 ImpCircUser* pU=(ImpCircUser*)rStat.GetUser();
755 delete pU;
756 rStat.SetUser(NULL);
759 bool SdrCircObj::BckCreate(SdrDragStat& rStat)
761 rStat.SetNoSnap(rStat.GetPointAnz()>=3);
762 rStat.SetOrtho4Possible(rStat.GetPointAnz()<3);
763 return meCircleKind!=OBJ_CIRC;
766 basegfx::B2DPolyPolygon SdrCircObj::TakeCreatePoly(const SdrDragStat& rDrag) const
768 ImpCircUser* pU = (ImpCircUser*)rDrag.GetUser();
770 if(rDrag.GetPointAnz() < 4L)
772 // force to OBJ_CIRC to get full visualisation
773 basegfx::B2DPolyPolygon aRetval(ImpCalcXPolyCirc(OBJ_CIRC, pU->aR, pU->nStart, pU->nEnd));
775 if(3L == rDrag.GetPointAnz())
777 // add edge to first point on ellipse
778 basegfx::B2DPolygon aNew;
780 aNew.append(basegfx::B2DPoint(pU->aCenter.X(), pU->aCenter.Y()));
781 aNew.append(basegfx::B2DPoint(pU->aP1.X(), pU->aP1.Y()));
782 aRetval.append(aNew);
785 return aRetval;
787 else
789 return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind, pU->aR, pU->nStart, pU->nEnd));
793 Pointer SdrCircObj::GetCreatePointer() const
795 switch (meCircleKind) {
796 case OBJ_CIRC: return Pointer(POINTER_DRAW_ELLIPSE);
797 case OBJ_SECT: return Pointer(POINTER_DRAW_PIE);
798 case OBJ_CARC: return Pointer(POINTER_DRAW_ARC);
799 case OBJ_CCUT: return Pointer(POINTER_DRAW_CIRCLECUT);
800 default: break;
801 } // switch
802 return Pointer(POINTER_CROSS);
805 void SdrCircObj::NbcMove(const Size& aSiz)
807 MoveRect(aRect,aSiz);
808 MoveRect(aOutRect,aSiz);
809 MoveRect(maSnapRect,aSiz);
810 SetXPolyDirty();
811 SetRectsDirty(true);
814 void SdrCircObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
816 long nWink0=aGeo.nDrehWink;
817 bool bNoShearRota=(aGeo.nDrehWink==0 && aGeo.nShearWink==0);
818 SdrTextObj::NbcResize(rRef,xFact,yFact);
819 bNoShearRota|=(aGeo.nDrehWink==0 && aGeo.nShearWink==0);
820 if (meCircleKind!=OBJ_CIRC) {
821 bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
822 bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
823 if (bXMirr || bYMirr) {
824 // At bXMirr!=bYMirr we should actually swap both line ends.
825 // That, however, is pretty bad (because of forced "hard" formatting).
826 // Alternatively, we could implement a bMirrored flag (maybe even
827 // a more general one, e. g. for mirrored text, ...).
828 long nS0=nStartWink;
829 long nE0=nEndWink;
830 if (bNoShearRota) {
831 // the RectObj already mirrors at VMirror because of a 180deg rotation
832 if (! (bXMirr && bYMirr)) {
833 long nTmp=nS0;
834 nS0=18000-nE0;
835 nE0=18000-nTmp;
837 } else { // mirror contorted ellipses
838 if (bXMirr!=bYMirr) {
839 nS0+=nWink0;
840 nE0+=nWink0;
841 if (bXMirr) {
842 long nTmp=nS0;
843 nS0=18000-nE0;
844 nE0=18000-nTmp;
846 if (bYMirr) {
847 long nTmp=nS0;
848 nS0=-nE0;
849 nE0=-nTmp;
851 nS0-=aGeo.nDrehWink;
852 nE0-=aGeo.nDrehWink;
855 long nWinkDif=nE0-nS0;
856 nStartWink=NormAngle360(nS0);
857 nEndWink =NormAngle360(nE0);
858 if (nWinkDif==36000) nEndWink+=nWinkDif; // full circle
861 SetXPolyDirty();
862 ImpSetCircInfoToAttr();
865 void SdrCircObj::NbcShear(const Point& rRef, long nWink, double tn, bool bVShear)
867 SdrTextObj::NbcShear(rRef,nWink,tn,bVShear);
868 SetXPolyDirty();
869 ImpSetCircInfoToAttr();
872 void SdrCircObj::NbcMirror(const Point& rRef1, const Point& rRef2)
874 bool bFreeMirr=meCircleKind!=OBJ_CIRC;
875 Point aTmpPt1;
876 Point aTmpPt2;
877 if (bFreeMirr) { // some preparations for using an arbitrary axis of reflection
878 Point aCenter(aRect.Center());
879 long nWdt=aRect.GetWidth()-1;
880 long nHgt=aRect.GetHeight()-1;
881 long nMaxRad=((nWdt>nHgt ? nWdt : nHgt)+1) /2;
882 double a;
883 // starting point
884 a=nStartWink*nPi180;
885 aTmpPt1=Point(Round(cos(a)*nMaxRad),-Round(sin(a)*nMaxRad));
886 if (nWdt==0) aTmpPt1.X()=0;
887 if (nHgt==0) aTmpPt1.Y()=0;
888 aTmpPt1+=aCenter;
889 // finishing point
890 a=nEndWink*nPi180;
891 aTmpPt2=Point(Round(cos(a)*nMaxRad),-Round(sin(a)*nMaxRad));
892 if (nWdt==0) aTmpPt2.X()=0;
893 if (nHgt==0) aTmpPt2.Y()=0;
894 aTmpPt2+=aCenter;
895 if (aGeo.nDrehWink!=0) {
896 RotatePoint(aTmpPt1,aRect.TopLeft(),aGeo.nSin,aGeo.nCos);
897 RotatePoint(aTmpPt2,aRect.TopLeft(),aGeo.nSin,aGeo.nCos);
899 if (aGeo.nShearWink!=0) {
900 ShearPoint(aTmpPt1,aRect.TopLeft(),aGeo.nTan);
901 ShearPoint(aTmpPt2,aRect.TopLeft(),aGeo.nTan);
904 SdrTextObj::NbcMirror(rRef1,rRef2);
905 if (meCircleKind!=OBJ_CIRC) { // adapt starting and finishing angle
906 MirrorPoint(aTmpPt1,rRef1,rRef2);
907 MirrorPoint(aTmpPt2,rRef1,rRef2);
908 // unrotate:
909 if (aGeo.nDrehWink!=0) {
910 RotatePoint(aTmpPt1,aRect.TopLeft(),-aGeo.nSin,aGeo.nCos); // -sin for reversion
911 RotatePoint(aTmpPt2,aRect.TopLeft(),-aGeo.nSin,aGeo.nCos); // -sin for reversion
913 // unshear:
914 if (aGeo.nShearWink!=0) {
915 ShearPoint(aTmpPt1,aRect.TopLeft(),-aGeo.nTan); // -tan for reversion
916 ShearPoint(aTmpPt2,aRect.TopLeft(),-aGeo.nTan); // -tan for reversion
918 Point aCenter(aRect.Center());
919 aTmpPt1-=aCenter;
920 aTmpPt2-=aCenter;
921 // because it's mirrored, the angles are swapped, too
922 nStartWink=GetAngle(aTmpPt2);
923 nEndWink =GetAngle(aTmpPt1);
924 long nWinkDif=nEndWink-nStartWink;
925 nStartWink=NormAngle360(nStartWink);
926 nEndWink =NormAngle360(nEndWink);
927 if (nWinkDif==36000) nEndWink+=nWinkDif; // full circle
929 SetXPolyDirty();
930 ImpSetCircInfoToAttr();
933 SdrObjGeoData* SdrCircObj::NewGeoData() const
935 return new SdrCircObjGeoData;
938 void SdrCircObj::SaveGeoData(SdrObjGeoData& rGeo) const
940 SdrRectObj::SaveGeoData(rGeo);
941 SdrCircObjGeoData& rCGeo=(SdrCircObjGeoData&)rGeo;
942 rCGeo.nStartWink=nStartWink;
943 rCGeo.nEndWink =nEndWink;
946 void SdrCircObj::RestGeoData(const SdrObjGeoData& rGeo)
948 SdrRectObj::RestGeoData(rGeo);
949 SdrCircObjGeoData& rCGeo=(SdrCircObjGeoData&)rGeo;
950 nStartWink=rCGeo.nStartWink;
951 nEndWink =rCGeo.nEndWink;
952 SetXPolyDirty();
953 ImpSetCircInfoToAttr();
956 void Union(Rectangle& rR, const Point& rP)
958 if (rP.X()<rR.Left ()) rR.Left ()=rP.X();
959 if (rP.X()>rR.Right ()) rR.Right ()=rP.X();
960 if (rP.Y()<rR.Top ()) rR.Top ()=rP.Y();
961 if (rP.Y()>rR.Bottom()) rR.Bottom()=rP.Y();
964 void SdrCircObj::TakeUnrotatedSnapRect(Rectangle& rRect) const
966 rRect=aRect;
967 if (meCircleKind!=OBJ_CIRC) {
968 const Point aPntStart(GetWinkPnt(aRect,nStartWink));
969 const Point aPntEnd(GetWinkPnt(aRect,nEndWink));
970 long a=nStartWink;
971 long e=nEndWink;
972 rRect.Left ()=aRect.Right();
973 rRect.Right ()=aRect.Left();
974 rRect.Top ()=aRect.Bottom();
975 rRect.Bottom()=aRect.Top();
976 Union(rRect,aPntStart);
977 Union(rRect,aPntEnd);
978 if ((a<=18000 && e>=18000) || (a>e && (a<=18000 || e>=18000))) {
979 Union(rRect,aRect.LeftCenter());
981 if ((a<=27000 && e>=27000) || (a>e && (a<=27000 || e>=27000))) {
982 Union(rRect,aRect.BottomCenter());
984 if (a>e) {
985 Union(rRect,aRect.RightCenter());
987 if ((a<=9000 && e>=9000) || (a>e && (a<=9000 || e>=9000))) {
988 Union(rRect,aRect.TopCenter());
990 if (meCircleKind==OBJ_SECT) {
991 Union(rRect,aRect.Center());
993 if (aGeo.nDrehWink!=0) {
994 Point aDst(rRect.TopLeft());
995 aDst-=aRect.TopLeft();
996 Point aDst0(aDst);
997 RotatePoint(aDst,Point(),aGeo.nSin,aGeo.nCos);
998 aDst-=aDst0;
999 rRect.Move(aDst.X(),aDst.Y());
1002 if (aGeo.nShearWink!=0) {
1003 long nDst=Round((rRect.Bottom()-rRect.Top())*aGeo.nTan);
1004 if (aGeo.nShearWink>0) {
1005 Point aRef(rRect.TopLeft());
1006 rRect.Left()-=nDst;
1007 Point aTmpPt(rRect.TopLeft());
1008 RotatePoint(aTmpPt,aRef,aGeo.nSin,aGeo.nCos);
1009 aTmpPt-=rRect.TopLeft();
1010 rRect.Move(aTmpPt.X(),aTmpPt.Y());
1011 } else {
1012 rRect.Right()-=nDst;
1017 void SdrCircObj::RecalcSnapRect()
1019 if (PaintNeedsXPolyCirc()) {
1020 maSnapRect=GetXPoly().GetBoundRect();
1021 } else {
1022 TakeUnrotatedSnapRect(maSnapRect);
1026 void SdrCircObj::NbcSetSnapRect(const Rectangle& rRect)
1028 if (aGeo.nDrehWink!=0 || aGeo.nShearWink!=0 || meCircleKind!=OBJ_CIRC) {
1029 Rectangle aSR0(GetSnapRect());
1030 long nWdt0=aSR0.Right()-aSR0.Left();
1031 long nHgt0=aSR0.Bottom()-aSR0.Top();
1032 long nWdt1=rRect.Right()-rRect.Left();
1033 long nHgt1=rRect.Bottom()-rRect.Top();
1034 NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
1035 NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
1036 } else {
1037 aRect=rRect;
1038 ImpJustifyRect(aRect);
1040 SetRectsDirty();
1041 SetXPolyDirty();
1042 ImpSetCircInfoToAttr();
1045 sal_uInt32 SdrCircObj::GetSnapPointCount() const
1047 if (meCircleKind==OBJ_CIRC) {
1048 return 1L;
1049 } else {
1050 return 3L;
1054 Point SdrCircObj::GetSnapPoint(sal_uInt32 i) const
1056 switch (i) {
1057 case 1 : return GetWinkPnt(aRect,nStartWink);
1058 case 2 : return GetWinkPnt(aRect,nEndWink);
1059 default: return aRect.Center();
1063 void SdrCircObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
1065 SetXPolyDirty();
1066 SdrRectObj::Notify(rBC,rHint);
1067 ImpSetAttrToCircInfo();
1072 void SdrCircObj::ImpSetAttrToCircInfo()
1074 const SfxItemSet& rSet = GetObjectItemSet();
1075 SdrCircKind eNewKindA = ((SdrCircKindItem&)rSet.Get(SDRATTR_CIRCKIND)).GetValue();
1076 SdrObjKind eNewKind = meCircleKind;
1078 if(eNewKindA == SDRCIRC_FULL)
1079 eNewKind = OBJ_CIRC;
1080 else if(eNewKindA == SDRCIRC_SECT)
1081 eNewKind = OBJ_SECT;
1082 else if(eNewKindA == SDRCIRC_ARC)
1083 eNewKind = OBJ_CARC;
1084 else if(eNewKindA == SDRCIRC_CUT)
1085 eNewKind = OBJ_CCUT;
1087 sal_Int32 nNewStart = ((SdrCircStartAngleItem&)rSet.Get(SDRATTR_CIRCSTARTANGLE)).GetValue();
1088 sal_Int32 nNewEnd = ((SdrCircEndAngleItem&)rSet.Get(SDRATTR_CIRCENDANGLE)).GetValue();
1090 bool bKindChg = meCircleKind != eNewKind;
1091 bool bWinkChg = nNewStart != nStartWink || nNewEnd != nEndWink;
1093 if(bKindChg || bWinkChg)
1095 meCircleKind = eNewKind;
1096 nStartWink = nNewStart;
1097 nEndWink = nNewEnd;
1099 if(bKindChg || (meCircleKind != OBJ_CIRC && bWinkChg))
1101 SetXPolyDirty();
1102 SetRectsDirty();
1107 void SdrCircObj::ImpSetCircInfoToAttr()
1109 SdrCircKind eNewKindA = SDRCIRC_FULL;
1110 const SfxItemSet& rSet = GetObjectItemSet();
1112 if(meCircleKind == OBJ_SECT)
1113 eNewKindA = SDRCIRC_SECT;
1114 else if(meCircleKind == OBJ_CARC)
1115 eNewKindA = SDRCIRC_ARC;
1116 else if(meCircleKind == OBJ_CCUT)
1117 eNewKindA = SDRCIRC_CUT;
1119 SdrCircKind eOldKindA = ((SdrCircKindItem&)rSet.Get(SDRATTR_CIRCKIND)).GetValue();
1120 sal_Int32 nOldStartWink = ((SdrCircStartAngleItem&)rSet.Get(SDRATTR_CIRCSTARTANGLE)).GetValue();
1121 sal_Int32 nOldEndWink = ((SdrCircEndAngleItem&)rSet.Get(SDRATTR_CIRCENDANGLE)).GetValue();
1123 if(eNewKindA != eOldKindA || nStartWink != nOldStartWink || nEndWink != nOldEndWink)
1125 // since SetItem() implicitly calls ImpSetAttrToCircInfo()
1126 // setting the item directly is necessary here.
1127 if(eNewKindA != eOldKindA)
1129 GetProperties().SetObjectItemDirect(SdrCircKindItem(eNewKindA));
1132 if(nStartWink != nOldStartWink)
1134 GetProperties().SetObjectItemDirect(SdrCircStartAngleItem(nStartWink));
1137 if(nEndWink != nOldEndWink)
1139 GetProperties().SetObjectItemDirect(SdrCircEndAngleItem(nEndWink));
1142 SetXPolyDirty();
1143 ImpSetAttrToCircInfo();
1147 SdrObject* SdrCircObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
1149 const bool bFill(OBJ_CARC == meCircleKind ? sal_False : sal_True);
1150 const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, aRect, nStartWink, nEndWink));
1151 SdrObject* pRet = ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon), bFill, bBezier);
1153 if(bAddText)
1155 pRet = ImpConvertAddText(pRet, bBezier);
1158 return pRet;
1161 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */