Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / svx / source / svdraw / svdocirc.cxx
blobab4cfd67c788f45660f368fde361bd78d02355fd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #include <svl/style.hxx>
30 #include <svx/xlnwtit.hxx>
31 #include <svx/xlnedwit.hxx>
32 #include <svx/xlnstwit.hxx>
33 #include <svx/xlnstit.hxx>
34 #include <svx/xlnedit.hxx>
35 #include <svx/svdocirc.hxx>
36 #include <math.h>
37 #include <svx/xpool.hxx>
38 #include <svx/svdattr.hxx>
39 #include <svx/svdpool.hxx>
40 #include <svx/svdattrx.hxx>
41 #include <svx/svdtrans.hxx>
42 #include <svx/svdetc.hxx>
43 #include <svx/svddrag.hxx>
44 #include <svx/svdmodel.hxx>
45 #include <svx/svdpage.hxx>
46 #include <svx/svdopath.hxx> // for the object conversion
47 #include <svx/svdview.hxx> // for dragging (Ortho)
48 #include "svx/svdglob.hxx" // StringCache
49 #include "svx/svdstr.hrc" // the object's name
50 #include <editeng/eeitem.hxx>
51 #include <svx/sdr/properties/circleproperties.hxx>
52 #include <svx/sdr/contact/viewcontactofsdrcircobj.hxx>
53 #include <basegfx/point/b2dpoint.hxx>
54 #include <basegfx/polygon/b2dpolygon.hxx>
55 #include <basegfx/polygon/b2dpolygontools.hxx>
56 #include <basegfx/matrix/b2dhommatrix.hxx>
57 #include <basegfx/matrix/b2dhommatrixtools.hxx>
59 //////////////////////////////////////////////////////////////////////////////
61 Point GetWinkPnt(const Rectangle& rR, long nWink)
63 Point aCenter(rR.Center());
64 long nWdt=rR.Right()-rR.Left();
65 long nHgt=rR.Bottom()-rR.Top();
66 long nMaxRad=((nWdt>nHgt ? nWdt : nHgt)+1) /2;
67 double a;
68 a=nWink*nPi180;
69 Point aRetval(Round(cos(a)*nMaxRad),-Round(sin(a)*nMaxRad));
70 if (nWdt==0) aRetval.X()=0;
71 if (nHgt==0) aRetval.Y()=0;
72 if (nWdt!=nHgt) {
73 if (nWdt>nHgt) {
74 if (nWdt!=0) {
75 // stop possible overruns for very large objects
76 if (Abs(nHgt)>32767 || Abs(aRetval.Y())>32767) {
77 aRetval.Y()=BigMulDiv(aRetval.Y(),nHgt,nWdt);
78 } else {
79 aRetval.Y()=aRetval.Y()*nHgt/nWdt;
82 } else {
83 if (nHgt!=0) {
84 // stop possible overruns for very large objects
85 if (Abs(nWdt)>32767 || Abs(aRetval.X())>32767) {
86 aRetval.X()=BigMulDiv(aRetval.X(),nWdt,nHgt);
87 } else {
88 aRetval.X()=aRetval.X()*nWdt/nHgt;
93 aRetval+=aCenter;
94 return aRetval;
97 //////////////////////////////////////////////////////////////////////////////
98 // BaseProperties section
100 sdr::properties::BaseProperties* SdrCircObj::CreateObjectSpecificProperties()
102 return new sdr::properties::CircleProperties(*this);
105 //////////////////////////////////////////////////////////////////////////////
106 // DrawContact section
108 sdr::contact::ViewContact* SdrCircObj::CreateObjectSpecificViewContact()
110 return new sdr::contact::ViewContactOfSdrCircObj(*this);
113 //////////////////////////////////////////////////////////////////////////////
115 TYPEINIT1(SdrCircObj,SdrRectObj);
117 SdrCircObj::SdrCircObj(SdrObjKind eNewKind)
119 nStartWink=0;
120 nEndWink=36000;
121 meCircleKind=eNewKind;
122 bClosedObj=eNewKind!=OBJ_CARC;
125 SdrCircObj::SdrCircObj(SdrObjKind eNewKind, const Rectangle& rRect):
126 SdrRectObj(rRect)
128 nStartWink=0;
129 nEndWink=36000;
130 meCircleKind=eNewKind;
131 bClosedObj=eNewKind!=OBJ_CARC;
134 SdrCircObj::SdrCircObj(SdrObjKind eNewKind, const Rectangle& rRect, long nNewStartWink, long nNewEndWink):
135 SdrRectObj(rRect)
137 long nWinkDif=nNewEndWink-nNewStartWink;
138 nStartWink=NormAngle360(nNewStartWink);
139 nEndWink=NormAngle360(nNewEndWink);
140 if (nWinkDif==36000) nEndWink+=nWinkDif; // full circle
141 meCircleKind=eNewKind;
142 bClosedObj=eNewKind!=OBJ_CARC;
145 SdrCircObj::~SdrCircObj()
149 void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
151 bool bCanConv=!HasText() || ImpCanConvTextToCurve();
152 rInfo.bEdgeRadiusAllowed = sal_False;
153 rInfo.bCanConvToPath=bCanConv;
154 rInfo.bCanConvToPoly=bCanConv;
155 rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
158 sal_uInt16 SdrCircObj::GetObjIdentifier() const
160 return sal_uInt16(meCircleKind);
163 bool SdrCircObj::PaintNeedsXPolyCirc() const
165 // XPoly is necessary for all rotated ellipse objects, circle and
166 // ellipse segments.
167 // If not WIN, then (for now) also for circle/ellipse segments and circle/
168 // ellipse arcs (for precision)
169 bool bNeed=aGeo.nDrehWink!=0 || aGeo.nShearWink!=0 || meCircleKind==OBJ_CCUT;
170 // If not WIN, then for everything except full circle (for now!)
171 if (meCircleKind!=OBJ_CIRC) bNeed = true;
173 const SfxItemSet& rSet = GetObjectItemSet();
174 if(!bNeed)
176 // XPoly is necessary for everything that isn't LineSolid or LineNone
177 XLineStyle eLine = ((XLineStyleItem&)(rSet.Get(XATTR_LINESTYLE))).GetValue();
178 bNeed = eLine != XLINE_NONE && eLine != XLINE_SOLID;
180 // XPoly is necessary for thick lines
181 if(!bNeed && eLine != XLINE_NONE)
182 bNeed = ((XLineWidthItem&)(rSet.Get(XATTR_LINEWIDTH))).GetValue() != 0;
184 // XPoly is necessary for circle arcs with line ends
185 if(!bNeed && meCircleKind == OBJ_CARC)
187 // start of the line is here if StartPolygon, StartWidth!=0
188 bNeed=((XLineStartItem&)(rSet.Get(XATTR_LINESTART))).GetLineStartValue().count() != 0L &&
189 ((XLineStartWidthItem&)(rSet.Get(XATTR_LINESTARTWIDTH))).GetValue() != 0;
191 if(!bNeed)
193 // end of the line is here if EndPolygon, EndWidth!=0
194 bNeed = ((XLineEndItem&)(rSet.Get(XATTR_LINEEND))).GetLineEndValue().count() != 0L &&
195 ((XLineEndWidthItem&)(rSet.Get(XATTR_LINEENDWIDTH))).GetValue() != 0;
200 // XPoly is necessary if Fill !=None and !=Solid
201 if(!bNeed && meCircleKind != OBJ_CARC)
203 XFillStyle eFill=((XFillStyleItem&)(rSet.Get(XATTR_FILLSTYLE))).GetValue();
204 bNeed = eFill != XFILL_NONE && eFill != XFILL_SOLID;
207 if(!bNeed && meCircleKind != OBJ_CIRC && nStartWink == nEndWink)
208 bNeed = true; // otherwise we're drawing a full circle
210 return bNeed;
213 basegfx::B2DPolygon SdrCircObj::ImpCalcXPolyCirc(const SdrObjKind eCicrleKind, const Rectangle& rRect1, long nStart, long nEnd) const
215 const basegfx::B2DRange aRange(rRect1.Left(), rRect1.Top(), rRect1.Right(), rRect1.Bottom());
216 basegfx::B2DPolygon aCircPolygon;
218 if(OBJ_CIRC == eCicrleKind)
220 // create full circle. Do not use createPolygonFromEllipse; it's necessary
221 // to get the start point to the bottom of the circle to keep compatible to
222 // old geometry creation
223 aCircPolygon = basegfx::tools::createPolygonFromUnitCircle(1);
225 // needs own scaling and translation from unit circle to target size (same as
226 // would be in createPolygonFromEllipse)
227 const basegfx::B2DPoint aCenter(aRange.getCenter());
228 const basegfx::B2DHomMatrix aMatrix(basegfx::tools::createScaleTranslateB2DHomMatrix(
229 aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
230 aCenter.getX(), aCenter.getY()));
231 aCircPolygon.transform(aMatrix);
233 else
235 // mirror start, end for geometry creation since model coordinate system is mirrored in Y
236 // #i111715# increase numerical correctness by first dividing and not using F_PI1800
237 const double fStart((((36000 - nEnd) % 36000) / 18000.0) * F_PI);
238 const double fEnd((((36000 - nStart) % 36000) / 18000.0) * F_PI);
240 // create circle segment. This is not closed by default
241 aCircPolygon = basegfx::tools::createPolygonFromEllipseSegment(
242 aRange.getCenter(), aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
243 fStart, fEnd);
245 // check closing states
246 const bool bCloseSegment(OBJ_CARC != eCicrleKind);
247 const bool bCloseUsingCenter(OBJ_SECT == eCicrleKind);
249 if(bCloseSegment)
251 if(bCloseUsingCenter)
253 // add center point at start (for historical reasons)
254 basegfx::B2DPolygon aSector;
255 aSector.append(aRange.getCenter());
256 aSector.append(aCircPolygon);
257 aCircPolygon = aSector;
260 // close
261 aCircPolygon.setClosed(true);
265 // #i76950#
266 if(aGeo.nShearWink || aGeo.nDrehWink)
268 // translate top left to (0,0)
269 const basegfx::B2DPoint aTopLeft(aRange.getMinimum());
270 basegfx::B2DHomMatrix aMatrix(basegfx::tools::createTranslateB2DHomMatrix(
271 -aTopLeft.getX(), -aTopLeft.getY()));
273 // shear, rotate and back to top left (if needed)
274 aMatrix = basegfx::tools::createShearXRotateTranslateB2DHomMatrix(
275 aGeo.nShearWink ? tan((36000 - aGeo.nShearWink) * F_PI18000) : 0.0,
276 aGeo.nDrehWink ? (36000 - aGeo.nDrehWink) * F_PI18000 : 0.0,
277 aTopLeft) * aMatrix;
279 // apply transformation
280 aCircPolygon.transform(aMatrix);
283 return aCircPolygon;
286 void SdrCircObj::RecalcXPoly()
288 const basegfx::B2DPolygon aPolyCirc(ImpCalcXPolyCirc(meCircleKind, aRect, nStartWink, nEndWink));
289 mpXPoly = new XPolygon(aPolyCirc);
292 void SdrCircObj::TakeObjNameSingul(XubString& rName) const
294 sal_uInt16 nID=STR_ObjNameSingulCIRC;
295 if (aRect.GetWidth()==aRect.GetHeight() && aGeo.nShearWink==0) {
296 switch (meCircleKind) {
297 case OBJ_CIRC: nID=STR_ObjNameSingulCIRC; break;
298 case OBJ_SECT: nID=STR_ObjNameSingulSECT; break;
299 case OBJ_CARC: nID=STR_ObjNameSingulCARC; break;
300 case OBJ_CCUT: nID=STR_ObjNameSingulCCUT; break;
301 default: break;
303 } else {
304 switch (meCircleKind) {
305 case OBJ_CIRC: nID=STR_ObjNameSingulCIRCE; break;
306 case OBJ_SECT: nID=STR_ObjNameSingulSECTE; break;
307 case OBJ_CARC: nID=STR_ObjNameSingulCARCE; break;
308 case OBJ_CCUT: nID=STR_ObjNameSingulCCUTE; break;
309 default: break;
312 rName=ImpGetResStr(nID);
314 String aName( GetName() );
315 if(aName.Len())
317 rName += sal_Unicode(' ');
318 rName += sal_Unicode('\'');
319 rName += aName;
320 rName += sal_Unicode('\'');
324 void SdrCircObj::TakeObjNamePlural(XubString& rName) const
326 sal_uInt16 nID=STR_ObjNamePluralCIRC;
327 if (aRect.GetWidth()==aRect.GetHeight() && aGeo.nShearWink==0) {
328 switch (meCircleKind) {
329 case OBJ_CIRC: nID=STR_ObjNamePluralCIRC; break;
330 case OBJ_SECT: nID=STR_ObjNamePluralSECT; break;
331 case OBJ_CARC: nID=STR_ObjNamePluralCARC; break;
332 case OBJ_CCUT: nID=STR_ObjNamePluralCCUT; break;
333 default: break;
335 } else {
336 switch (meCircleKind) {
337 case OBJ_CIRC: nID=STR_ObjNamePluralCIRCE; break;
338 case OBJ_SECT: nID=STR_ObjNamePluralSECTE; break;
339 case OBJ_CARC: nID=STR_ObjNamePluralCARCE; break;
340 case OBJ_CCUT: nID=STR_ObjNamePluralCCUTE; break;
341 default: break;
344 rName=ImpGetResStr(nID);
347 SdrCircObj* SdrCircObj::Clone() const
349 return CloneHelper< SdrCircObj >();
352 basegfx::B2DPolyPolygon SdrCircObj::TakeXorPoly() const
354 const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, aRect, nStartWink, nEndWink));
355 return basegfx::B2DPolyPolygon(aCircPolygon);
358 struct ImpCircUser : public SdrDragStatUserData
360 Rectangle aR;
361 Point aCenter;
362 Point aRadius;
363 Point aP1;
364 Point aP2;
365 long nMaxRad;
366 long nHgt;
367 long nWdt;
368 long nStart;
369 long nEnd;
370 long nWink;
371 bool bRight; // not yet implemented
373 public:
374 ImpCircUser()
375 : nMaxRad(0),
376 nHgt(0),
377 nWdt(0),
378 nStart(0),
379 nEnd(0),
380 nWink(0),
381 bRight(sal_False)
383 void SetCreateParams(SdrDragStat& rStat);
386 sal_uInt32 SdrCircObj::GetHdlCount() const
388 if(OBJ_CIRC != meCircleKind)
390 return 10L;
392 else
394 return 8L;
398 SdrHdl* SdrCircObj::GetHdl(sal_uInt32 nHdlNum) const
400 if (meCircleKind==OBJ_CIRC)
402 nHdlNum += 2L;
405 SdrHdl* pH = NULL;
406 Point aPnt;
407 SdrHdlKind eLocalKind(HDL_MOVE);
408 sal_uInt32 nPNum(0);
410 switch (nHdlNum)
412 case 0:
413 aPnt = GetWinkPnt(aRect,nStartWink);
414 eLocalKind = HDL_CIRC;
415 nPNum = 1;
416 break;
417 case 1:
418 aPnt = GetWinkPnt(aRect,nEndWink);
419 eLocalKind = HDL_CIRC;
420 nPNum = 2L;
421 break;
422 case 2:
423 aPnt = aRect.TopLeft();
424 eLocalKind = HDL_UPLFT;
425 break;
426 case 3:
427 aPnt = aRect.TopCenter();
428 eLocalKind = HDL_UPPER;
429 break;
430 case 4:
431 aPnt = aRect.TopRight();
432 eLocalKind = HDL_UPRGT;
433 break;
434 case 5:
435 aPnt = aRect.LeftCenter();
436 eLocalKind = HDL_LEFT;
437 break;
438 case 6:
439 aPnt = aRect.RightCenter();
440 eLocalKind = HDL_RIGHT;
441 break;
442 case 7:
443 aPnt = aRect.BottomLeft();
444 eLocalKind = HDL_LWLFT;
445 break;
446 case 8:
447 aPnt = aRect.BottomCenter();
448 eLocalKind = HDL_LOWER;
449 break;
450 case 9:
451 aPnt = aRect.BottomRight();
452 eLocalKind = HDL_LWRGT;
453 break;
456 if (aGeo.nShearWink)
458 ShearPoint(aPnt,aRect.TopLeft(),aGeo.nTan);
461 if (aGeo.nDrehWink)
463 RotatePoint(aPnt,aRect.TopLeft(),aGeo.nSin,aGeo.nCos);
466 if (eLocalKind != HDL_MOVE)
468 pH = new SdrHdl(aPnt,eLocalKind);
469 pH->SetPointNum(nPNum);
470 pH->SetObj((SdrObject*)this);
471 pH->SetDrehWink(aGeo.nDrehWink);
474 return pH;
477 ////////////////////////////////////////////////////////////////////////////////////////////////////
479 bool SdrCircObj::hasSpecialDrag() const
481 return true;
484 bool SdrCircObj::beginSpecialDrag(SdrDragStat& rDrag) const
486 const bool bWink(rDrag.GetHdl() && HDL_CIRC == rDrag.GetHdl()->GetKind());
488 if(bWink)
490 if(1 == rDrag.GetHdl()->GetPointNum() || 2 == rDrag.GetHdl()->GetPointNum())
492 rDrag.SetNoSnap(true);
495 return true;
498 return SdrTextObj::beginSpecialDrag(rDrag);
501 bool SdrCircObj::applySpecialDrag(SdrDragStat& rDrag)
503 const bool bWink(rDrag.GetHdl() && HDL_CIRC == rDrag.GetHdl()->GetKind());
505 if(bWink)
507 Point aPt(rDrag.GetNow());
509 if (aGeo.nDrehWink!=0)
510 RotatePoint(aPt,aRect.TopLeft(),-aGeo.nSin,aGeo.nCos);
512 if (aGeo.nShearWink!=0)
513 ShearPoint(aPt,aRect.TopLeft(),-aGeo.nTan);
515 aPt-=aRect.Center();
517 long nWdt=aRect.Right()-aRect.Left();
518 long nHgt=aRect.Bottom()-aRect.Top();
520 if(nWdt>=nHgt)
522 aPt.Y()=BigMulDiv(aPt.Y(),nWdt,nHgt);
524 else
526 aPt.X()=BigMulDiv(aPt.X(),nHgt,nWdt);
529 long nWink=NormAngle360(GetAngle(aPt));
531 if (rDrag.GetView() && rDrag.GetView()->IsAngleSnapEnabled())
533 long nSA=rDrag.GetView()->GetSnapAngle();
535 if (nSA!=0)
537 nWink+=nSA/2;
538 nWink/=nSA;
539 nWink*=nSA;
540 nWink=NormAngle360(nWink);
544 if(1 == rDrag.GetHdl()->GetPointNum())
546 nStartWink = nWink;
548 else if(2 == rDrag.GetHdl()->GetPointNum())
550 nEndWink = nWink;
553 SetRectsDirty();
554 SetXPolyDirty();
555 ImpSetCircInfoToAttr();
556 SetChanged();
558 return true;
560 else
562 return SdrTextObj::applySpecialDrag(rDrag);
566 String SdrCircObj::getSpecialDragComment(const SdrDragStat& rDrag) const
568 const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
570 if(bCreateComment)
572 rtl::OUString aStr;
573 ImpTakeDescriptionStr(STR_ViewCreateObj, aStr);
574 rtl::OUStringBuffer aBuf(aStr);
575 const sal_uInt32 nPntAnz(rDrag.GetPointAnz());
577 if(OBJ_CIRC != meCircleKind && nPntAnz > 2)
579 ImpCircUser* pU = (ImpCircUser*)rDrag.GetUser();
580 sal_Int32 nWink;
582 aBuf.appendAscii(" (");
584 if(3 == nPntAnz)
586 nWink = pU->nStart;
588 else
590 nWink = pU->nEnd;
593 aBuf.append(GetWinkStr(nWink,false));
594 aBuf.append(sal_Unicode(')'));
597 return aBuf.makeStringAndClear();
599 else
601 const bool bWink(rDrag.GetHdl() && HDL_CIRC == rDrag.GetHdl()->GetKind());
603 if(bWink)
605 const sal_Int32 nWink(1 == rDrag.GetHdl()->GetPointNum() ? nStartWink : nEndWink);
607 rtl::OUString aStr;
608 ImpTakeDescriptionStr(STR_DragCircAngle, aStr);
609 rtl::OUStringBuffer aBuf(aStr);
610 aBuf.appendAscii(" (");
611 aBuf.append(GetWinkStr(nWink,false));
612 aBuf.append(sal_Unicode(')'));
614 return aBuf.makeStringAndClear();
616 else
618 return SdrTextObj::getSpecialDragComment(rDrag);
623 ////////////////////////////////////////////////////////////////////////////////////////////////////
625 void ImpCircUser::SetCreateParams(SdrDragStat& rStat)
627 rStat.TakeCreateRect(aR);
628 aR.Justify();
629 aCenter=aR.Center();
630 nWdt=aR.Right()-aR.Left();
631 nHgt=aR.Bottom()-aR.Top();
632 nMaxRad=((nWdt>nHgt ? nWdt : nHgt)+1) /2;
633 nStart=0;
634 nEnd=36000;
635 if (rStat.GetPointAnz()>2) {
636 Point aP(rStat.GetPoint(2)-aCenter);
637 if (nWdt==0) aP.X()=0;
638 if (nHgt==0) aP.Y()=0;
639 if (nWdt>=nHgt) {
640 if (nHgt!=0) aP.Y()=aP.Y()*nWdt/nHgt;
641 } else {
642 if (nWdt!=0) aP.X()=aP.X()*nHgt/nWdt;
644 nStart=NormAngle360(GetAngle(aP));
645 if (rStat.GetView()!=NULL && rStat.GetView()->IsAngleSnapEnabled()) {
646 long nSA=rStat.GetView()->GetSnapAngle();
647 if (nSA!=0) { // angle snapping
648 nStart+=nSA/2;
649 nStart/=nSA;
650 nStart*=nSA;
651 nStart=NormAngle360(nStart);
654 aP1 = GetWinkPnt(aR,nStart);
655 nEnd=nStart;
656 aP2=aP1;
657 } else aP1=aCenter;
658 if (rStat.GetPointAnz()>3) {
659 Point aP(rStat.GetPoint(3)-aCenter);
660 if (nWdt>=nHgt) {
661 aP.Y()=BigMulDiv(aP.Y(),nWdt,nHgt);
662 } else {
663 aP.X()=BigMulDiv(aP.X(),nHgt,nWdt);
665 nEnd=NormAngle360(GetAngle(aP));
666 if (rStat.GetView()!=NULL && rStat.GetView()->IsAngleSnapEnabled()) {
667 long nSA=rStat.GetView()->GetSnapAngle();
668 if (nSA!=0) { // angle snapping
669 nEnd+=nSA/2;
670 nEnd/=nSA;
671 nEnd*=nSA;
672 nEnd=NormAngle360(nEnd);
675 aP2 = GetWinkPnt(aR,nEnd);
676 } else aP2=aCenter;
679 void SdrCircObj::ImpSetCreateParams(SdrDragStat& rStat) const
681 ImpCircUser* pU=(ImpCircUser*)rStat.GetUser();
682 if (pU==NULL) {
683 pU=new ImpCircUser;
684 rStat.SetUser(pU);
686 pU->SetCreateParams(rStat);
689 bool SdrCircObj::BegCreate(SdrDragStat& rStat)
691 rStat.SetOrtho4Possible();
692 Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
693 aRect1.Justify();
694 rStat.SetActionRect(aRect1);
695 aRect = aRect1;
696 ImpSetCreateParams(rStat);
697 return sal_True;
700 bool SdrCircObj::MovCreate(SdrDragStat& rStat)
702 ImpSetCreateParams(rStat);
703 ImpCircUser* pU=(ImpCircUser*)rStat.GetUser();
704 rStat.SetActionRect(pU->aR);
705 aRect=pU->aR; // for ObjName
706 ImpJustifyRect(aRect);
707 nStartWink=pU->nStart;
708 nEndWink=pU->nEnd;
709 SetBoundRectDirty();
710 bSnapRectDirty=sal_True;
711 SetXPolyDirty();
713 // #i103058# push current angle settings to ItemSet to
714 // allow FullDrag visualisation
715 if(rStat.GetPointAnz() >= 4)
717 ImpSetCircInfoToAttr();
720 return sal_True;
723 bool SdrCircObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
725 ImpSetCreateParams(rStat);
726 ImpCircUser* pU=(ImpCircUser*)rStat.GetUser();
727 bool bRet = false;
728 if (eCmd==SDRCREATE_FORCEEND && rStat.GetPointAnz()<4) meCircleKind=OBJ_CIRC;
729 if (meCircleKind==OBJ_CIRC) {
730 bRet=rStat.GetPointAnz()>=2;
731 if (bRet) {
732 aRect=pU->aR;
733 ImpJustifyRect(aRect);
735 } else {
736 rStat.SetNoSnap(rStat.GetPointAnz()>=2);
737 rStat.SetOrtho4Possible(rStat.GetPointAnz()<2);
738 bRet=rStat.GetPointAnz()>=4;
739 if (bRet) {
740 aRect=pU->aR;
741 ImpJustifyRect(aRect);
742 nStartWink=pU->nStart;
743 nEndWink=pU->nEnd;
746 bClosedObj=meCircleKind!=OBJ_CARC;
747 SetRectsDirty();
748 SetXPolyDirty();
749 ImpSetCircInfoToAttr();
750 if (bRet) {
751 delete pU;
752 rStat.SetUser(NULL);
754 return bRet;
757 void SdrCircObj::BrkCreate(SdrDragStat& rStat)
759 ImpCircUser* pU=(ImpCircUser*)rStat.GetUser();
760 delete pU;
761 rStat.SetUser(NULL);
764 bool SdrCircObj::BckCreate(SdrDragStat& rStat)
766 rStat.SetNoSnap(rStat.GetPointAnz()>=3);
767 rStat.SetOrtho4Possible(rStat.GetPointAnz()<3);
768 return meCircleKind!=OBJ_CIRC;
771 basegfx::B2DPolyPolygon SdrCircObj::TakeCreatePoly(const SdrDragStat& rDrag) const
773 ImpCircUser* pU = (ImpCircUser*)rDrag.GetUser();
775 if(rDrag.GetPointAnz() < 4L)
777 // force to OBJ_CIRC to get full visualisation
778 basegfx::B2DPolyPolygon aRetval(ImpCalcXPolyCirc(OBJ_CIRC, pU->aR, pU->nStart, pU->nEnd));
780 if(3L == rDrag.GetPointAnz())
782 // add edge to first point on ellipse
783 basegfx::B2DPolygon aNew;
785 aNew.append(basegfx::B2DPoint(pU->aCenter.X(), pU->aCenter.Y()));
786 aNew.append(basegfx::B2DPoint(pU->aP1.X(), pU->aP1.Y()));
787 aRetval.append(aNew);
790 return aRetval;
792 else
794 return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind, pU->aR, pU->nStart, pU->nEnd));
798 Pointer SdrCircObj::GetCreatePointer() const
800 switch (meCircleKind) {
801 case OBJ_CIRC: return Pointer(POINTER_DRAW_ELLIPSE);
802 case OBJ_SECT: return Pointer(POINTER_DRAW_PIE);
803 case OBJ_CARC: return Pointer(POINTER_DRAW_ARC);
804 case OBJ_CCUT: return Pointer(POINTER_DRAW_CIRCLECUT);
805 default: break;
806 } // switch
807 return Pointer(POINTER_CROSS);
810 void SdrCircObj::NbcMove(const Size& aSiz)
812 MoveRect(aRect,aSiz);
813 MoveRect(aOutRect,aSiz);
814 MoveRect(maSnapRect,aSiz);
815 SetXPolyDirty();
816 SetRectsDirty(sal_True);
819 void SdrCircObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
821 long nWink0=aGeo.nDrehWink;
822 bool bNoShearRota=(aGeo.nDrehWink==0 && aGeo.nShearWink==0);
823 SdrTextObj::NbcResize(rRef,xFact,yFact);
824 bNoShearRota|=(aGeo.nDrehWink==0 && aGeo.nShearWink==0);
825 if (meCircleKind!=OBJ_CIRC) {
826 bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
827 bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
828 if (bXMirr || bYMirr) {
829 // At bXMirr!=bYMirr we should actually swap both line ends.
830 // That, however, is pretty bad (because of forced "hard" formatting).
831 // Alternatively, we could implement a bMirrored flag (maybe even
832 // a more general one, e. g. for mirrored text, ...).
833 long nS0=nStartWink;
834 long nE0=nEndWink;
835 if (bNoShearRota) {
836 // the RectObj already mirrors at VMirror because of a 180deg rotation
837 if (! (bXMirr && bYMirr)) {
838 long nTmp=nS0;
839 nS0=18000-nE0;
840 nE0=18000-nTmp;
842 } else { // mirror contorted ellipses
843 if (bXMirr!=bYMirr) {
844 nS0+=nWink0;
845 nE0+=nWink0;
846 if (bXMirr) {
847 long nTmp=nS0;
848 nS0=18000-nE0;
849 nE0=18000-nTmp;
851 if (bYMirr) {
852 long nTmp=nS0;
853 nS0=-nE0;
854 nE0=-nTmp;
856 nS0-=aGeo.nDrehWink;
857 nE0-=aGeo.nDrehWink;
860 long nWinkDif=nE0-nS0;
861 nStartWink=NormAngle360(nS0);
862 nEndWink =NormAngle360(nE0);
863 if (nWinkDif==36000) nEndWink+=nWinkDif; // full circle
866 SetXPolyDirty();
867 ImpSetCircInfoToAttr();
870 void SdrCircObj::NbcShear(const Point& rRef, long nWink, double tn, bool bVShear)
872 SdrTextObj::NbcShear(rRef,nWink,tn,bVShear);
873 SetXPolyDirty();
874 ImpSetCircInfoToAttr();
877 void SdrCircObj::NbcMirror(const Point& rRef1, const Point& rRef2)
879 bool bFreeMirr=meCircleKind!=OBJ_CIRC;
880 Point aTmpPt1;
881 Point aTmpPt2;
882 if (bFreeMirr) { // some preparations for using an arbitrary axis of reflection
883 Point aCenter(aRect.Center());
884 long nWdt=aRect.GetWidth()-1;
885 long nHgt=aRect.GetHeight()-1;
886 long nMaxRad=((nWdt>nHgt ? nWdt : nHgt)+1) /2;
887 double a;
888 // starting point
889 a=nStartWink*nPi180;
890 aTmpPt1=Point(Round(cos(a)*nMaxRad),-Round(sin(a)*nMaxRad));
891 if (nWdt==0) aTmpPt1.X()=0;
892 if (nHgt==0) aTmpPt1.Y()=0;
893 aTmpPt1+=aCenter;
894 // finishing point
895 a=nEndWink*nPi180;
896 aTmpPt2=Point(Round(cos(a)*nMaxRad),-Round(sin(a)*nMaxRad));
897 if (nWdt==0) aTmpPt2.X()=0;
898 if (nHgt==0) aTmpPt2.Y()=0;
899 aTmpPt2+=aCenter;
900 if (aGeo.nDrehWink!=0) {
901 RotatePoint(aTmpPt1,aRect.TopLeft(),aGeo.nSin,aGeo.nCos);
902 RotatePoint(aTmpPt2,aRect.TopLeft(),aGeo.nSin,aGeo.nCos);
904 if (aGeo.nShearWink!=0) {
905 ShearPoint(aTmpPt1,aRect.TopLeft(),aGeo.nTan);
906 ShearPoint(aTmpPt2,aRect.TopLeft(),aGeo.nTan);
909 SdrTextObj::NbcMirror(rRef1,rRef2);
910 if (meCircleKind!=OBJ_CIRC) { // adapt starting and finishing angle
911 MirrorPoint(aTmpPt1,rRef1,rRef2);
912 MirrorPoint(aTmpPt2,rRef1,rRef2);
913 // unrotate:
914 if (aGeo.nDrehWink!=0) {
915 RotatePoint(aTmpPt1,aRect.TopLeft(),-aGeo.nSin,aGeo.nCos); // -sin for reversion
916 RotatePoint(aTmpPt2,aRect.TopLeft(),-aGeo.nSin,aGeo.nCos); // -sin for reversion
918 // unshear:
919 if (aGeo.nShearWink!=0) {
920 ShearPoint(aTmpPt1,aRect.TopLeft(),-aGeo.nTan); // -tan for reversion
921 ShearPoint(aTmpPt2,aRect.TopLeft(),-aGeo.nTan); // -tan for reversion
923 Point aCenter(aRect.Center());
924 aTmpPt1-=aCenter;
925 aTmpPt2-=aCenter;
926 // because it's mirrored, the angles are swapped, too
927 nStartWink=GetAngle(aTmpPt2);
928 nEndWink =GetAngle(aTmpPt1);
929 long nWinkDif=nEndWink-nStartWink;
930 nStartWink=NormAngle360(nStartWink);
931 nEndWink =NormAngle360(nEndWink);
932 if (nWinkDif==36000) nEndWink+=nWinkDif; // full circle
934 SetXPolyDirty();
935 ImpSetCircInfoToAttr();
938 SdrObjGeoData* SdrCircObj::NewGeoData() const
940 return new SdrCircObjGeoData;
943 void SdrCircObj::SaveGeoData(SdrObjGeoData& rGeo) const
945 SdrRectObj::SaveGeoData(rGeo);
946 SdrCircObjGeoData& rCGeo=(SdrCircObjGeoData&)rGeo;
947 rCGeo.nStartWink=nStartWink;
948 rCGeo.nEndWink =nEndWink;
951 void SdrCircObj::RestGeoData(const SdrObjGeoData& rGeo)
953 SdrRectObj::RestGeoData(rGeo);
954 SdrCircObjGeoData& rCGeo=(SdrCircObjGeoData&)rGeo;
955 nStartWink=rCGeo.nStartWink;
956 nEndWink =rCGeo.nEndWink;
957 SetXPolyDirty();
958 ImpSetCircInfoToAttr();
961 void Union(Rectangle& rR, const Point& rP)
963 if (rP.X()<rR.Left ()) rR.Left ()=rP.X();
964 if (rP.X()>rR.Right ()) rR.Right ()=rP.X();
965 if (rP.Y()<rR.Top ()) rR.Top ()=rP.Y();
966 if (rP.Y()>rR.Bottom()) rR.Bottom()=rP.Y();
969 void SdrCircObj::TakeUnrotatedSnapRect(Rectangle& rRect) const
971 rRect=aRect;
972 if (meCircleKind!=OBJ_CIRC) {
973 const Point aPntStart(GetWinkPnt(aRect,nStartWink));
974 const Point aPntEnd(GetWinkPnt(aRect,nEndWink));
975 long a=nStartWink;
976 long e=nEndWink;
977 rRect.Left ()=aRect.Right();
978 rRect.Right ()=aRect.Left();
979 rRect.Top ()=aRect.Bottom();
980 rRect.Bottom()=aRect.Top();
981 Union(rRect,aPntStart);
982 Union(rRect,aPntEnd);
983 if ((a<=18000 && e>=18000) || (a>e && (a<=18000 || e>=18000))) {
984 Union(rRect,aRect.LeftCenter());
986 if ((a<=27000 && e>=27000) || (a>e && (a<=27000 || e>=27000))) {
987 Union(rRect,aRect.BottomCenter());
989 if (a>e) {
990 Union(rRect,aRect.RightCenter());
992 if ((a<=9000 && e>=9000) || (a>e && (a<=9000 || e>=9000))) {
993 Union(rRect,aRect.TopCenter());
995 if (meCircleKind==OBJ_SECT) {
996 Union(rRect,aRect.Center());
998 if (aGeo.nDrehWink!=0) {
999 Point aDst(rRect.TopLeft());
1000 aDst-=aRect.TopLeft();
1001 Point aDst0(aDst);
1002 RotatePoint(aDst,Point(),aGeo.nSin,aGeo.nCos);
1003 aDst-=aDst0;
1004 rRect.Move(aDst.X(),aDst.Y());
1007 if (aGeo.nShearWink!=0) {
1008 long nDst=Round((rRect.Bottom()-rRect.Top())*aGeo.nTan);
1009 if (aGeo.nShearWink>0) {
1010 Point aRef(rRect.TopLeft());
1011 rRect.Left()-=nDst;
1012 Point aTmpPt(rRect.TopLeft());
1013 RotatePoint(aTmpPt,aRef,aGeo.nSin,aGeo.nCos);
1014 aTmpPt-=rRect.TopLeft();
1015 rRect.Move(aTmpPt.X(),aTmpPt.Y());
1016 } else {
1017 rRect.Right()-=nDst;
1022 void SdrCircObj::RecalcSnapRect()
1024 if (PaintNeedsXPolyCirc()) {
1025 maSnapRect=GetXPoly().GetBoundRect();
1026 } else {
1027 TakeUnrotatedSnapRect(maSnapRect);
1031 void SdrCircObj::NbcSetSnapRect(const Rectangle& rRect)
1033 if (aGeo.nDrehWink!=0 || aGeo.nShearWink!=0 || meCircleKind!=OBJ_CIRC) {
1034 Rectangle aSR0(GetSnapRect());
1035 long nWdt0=aSR0.Right()-aSR0.Left();
1036 long nHgt0=aSR0.Bottom()-aSR0.Top();
1037 long nWdt1=rRect.Right()-rRect.Left();
1038 long nHgt1=rRect.Bottom()-rRect.Top();
1039 NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
1040 NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
1041 } else {
1042 aRect=rRect;
1043 ImpJustifyRect(aRect);
1045 SetRectsDirty();
1046 SetXPolyDirty();
1047 ImpSetCircInfoToAttr();
1050 sal_uInt32 SdrCircObj::GetSnapPointCount() const
1052 if (meCircleKind==OBJ_CIRC) {
1053 return 1L;
1054 } else {
1055 return 3L;
1059 Point SdrCircObj::GetSnapPoint(sal_uInt32 i) const
1061 switch (i) {
1062 case 1 : return GetWinkPnt(aRect,nStartWink);
1063 case 2 : return GetWinkPnt(aRect,nEndWink);
1064 default: return aRect.Center();
1068 void SdrCircObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
1070 SetXPolyDirty();
1071 SdrRectObj::Notify(rBC,rHint);
1072 ImpSetAttrToCircInfo();
1075 ////////////////////////////////////////////////////////////////////////////////////////////////////
1077 void SdrCircObj::ImpSetAttrToCircInfo()
1079 const SfxItemSet& rSet = GetObjectItemSet();
1080 SdrCircKind eNewKindA = ((SdrCircKindItem&)rSet.Get(SDRATTR_CIRCKIND)).GetValue();
1081 SdrObjKind eNewKind = meCircleKind;
1083 if(eNewKindA == SDRCIRC_FULL)
1084 eNewKind = OBJ_CIRC;
1085 else if(eNewKindA == SDRCIRC_SECT)
1086 eNewKind = OBJ_SECT;
1087 else if(eNewKindA == SDRCIRC_ARC)
1088 eNewKind = OBJ_CARC;
1089 else if(eNewKindA == SDRCIRC_CUT)
1090 eNewKind = OBJ_CCUT;
1092 sal_Int32 nNewStart = ((SdrCircStartAngleItem&)rSet.Get(SDRATTR_CIRCSTARTANGLE)).GetValue();
1093 sal_Int32 nNewEnd = ((SdrCircEndAngleItem&)rSet.Get(SDRATTR_CIRCENDANGLE)).GetValue();
1095 sal_Bool bKindChg = meCircleKind != eNewKind;
1096 sal_Bool bWinkChg = nNewStart != nStartWink || nNewEnd != nEndWink;
1098 if(bKindChg || bWinkChg)
1100 meCircleKind = eNewKind;
1101 nStartWink = nNewStart;
1102 nEndWink = nNewEnd;
1104 if(bKindChg || (meCircleKind != OBJ_CIRC && bWinkChg))
1106 SetXPolyDirty();
1107 SetRectsDirty();
1112 void SdrCircObj::ImpSetCircInfoToAttr()
1114 SdrCircKind eNewKindA = SDRCIRC_FULL;
1115 const SfxItemSet& rSet = GetObjectItemSet();
1117 if(meCircleKind == OBJ_SECT)
1118 eNewKindA = SDRCIRC_SECT;
1119 else if(meCircleKind == OBJ_CARC)
1120 eNewKindA = SDRCIRC_ARC;
1121 else if(meCircleKind == OBJ_CCUT)
1122 eNewKindA = SDRCIRC_CUT;
1124 SdrCircKind eOldKindA = ((SdrCircKindItem&)rSet.Get(SDRATTR_CIRCKIND)).GetValue();
1125 sal_Int32 nOldStartWink = ((SdrCircStartAngleItem&)rSet.Get(SDRATTR_CIRCSTARTANGLE)).GetValue();
1126 sal_Int32 nOldEndWink = ((SdrCircEndAngleItem&)rSet.Get(SDRATTR_CIRCENDANGLE)).GetValue();
1128 if(eNewKindA != eOldKindA || nStartWink != nOldStartWink || nEndWink != nOldEndWink)
1130 // since SetItem() implicitly calls ImpSetAttrToCircInfo()
1131 // setting the item directly is necessary here.
1132 if(eNewKindA != eOldKindA)
1134 GetProperties().SetObjectItemDirect(SdrCircKindItem(eNewKindA));
1137 if(nStartWink != nOldStartWink)
1139 GetProperties().SetObjectItemDirect(SdrCircStartAngleItem(nStartWink));
1142 if(nEndWink != nOldEndWink)
1144 GetProperties().SetObjectItemDirect(SdrCircEndAngleItem(nEndWink));
1147 SetXPolyDirty();
1148 ImpSetAttrToCircInfo();
1152 SdrObject* SdrCircObj::DoConvertToPolyObj(sal_Bool bBezier) const
1154 const sal_Bool bFill(OBJ_CARC == meCircleKind ? sal_False : sal_True);
1155 const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, aRect, nStartWink, nEndWink));
1156 SdrObject* pRet = ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon), bFill, bBezier);
1157 pRet = ImpConvertAddText(pRet, bBezier);
1159 return pRet;
1162 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */