/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; static Point GetAnglePnt(const tools::Rectangle& rR, Degree100 nAngle) { Point aCenter(rR.Center()); tools::Long nWdt=rR.Right()-rR.Left(); tools::Long nHgt=rR.Bottom()-rR.Top(); tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2; double a = toRadians(nAngle); Point aRetval(basegfx::fround(cos(a) * nMaxRad), basegfx::fround(-sin(a) * nMaxRad)); if (nWdt==0) aRetval.setX(0 ); if (nHgt==0) aRetval.setY(0 ); if (nWdt!=nHgt) { if (nWdt>nHgt) { if (nWdt!=0) { // stop possible overruns for very large objects if (std::abs(nHgt)>32767 || std::abs(aRetval.Y())>32767) { aRetval.setY(BigMulDiv(aRetval.Y(),nHgt,nWdt) ); } else { aRetval.setY(aRetval.Y()*nHgt/nWdt ); } } } else { if (nHgt!=0) { // stop possible overruns for very large objects if (std::abs(nWdt)>32767 || std::abs(aRetval.X())>32767) { aRetval.setX(BigMulDiv(aRetval.X(),nWdt,nHgt) ); } else { aRetval.setX(aRetval.X()*nWdt/nHgt ); } } } } aRetval+=aCenter; return aRetval; } // BaseProperties section std::unique_ptr SdrCircObj::CreateObjectSpecificProperties() { return std::make_unique(*this); } // DrawContact section std::unique_ptr SdrCircObj::CreateObjectSpecificViewContact() { return std::make_unique(*this); } SdrCircKind ToSdrCircKind(SdrObjKind eKind) { switch (eKind) { case SdrObjKind::CircleOrEllipse: return SdrCircKind::Full; case SdrObjKind::CircleSection: return SdrCircKind::Section; case SdrObjKind::CircleArc: return SdrCircKind::Arc; case SdrObjKind::CircleCut: return SdrCircKind::Cut; default: assert(false); } return SdrCircKind::Full; } SdrCircObj::SdrCircObj( SdrModel& rSdrModel, SdrCircKind eNewKind) : SdrRectObj(rSdrModel) { nStartAngle=0_deg100; nEndAngle=36000_deg100; meCircleKind=eNewKind; m_bClosedObj=eNewKind!=SdrCircKind::Arc; } SdrCircObj::SdrCircObj(SdrModel& rSdrModel, SdrCircObj const & rSource) : SdrRectObj(rSdrModel, rSource) { meCircleKind = rSource.meCircleKind; nStartAngle = rSource.nStartAngle; nEndAngle = rSource.nEndAngle; m_bClosedObj = rSource.m_bClosedObj; } SdrCircObj::SdrCircObj( SdrModel& rSdrModel, SdrCircKind eNewKind, const tools::Rectangle& rRect) : SdrRectObj(rSdrModel, rRect) { nStartAngle=0_deg100; nEndAngle=36000_deg100; meCircleKind=eNewKind; m_bClosedObj=eNewKind!=SdrCircKind::Arc; } SdrCircObj::SdrCircObj( SdrModel& rSdrModel, SdrCircKind eNewKind, const tools::Rectangle& rRect, Degree100 nNewStartAngle, Degree100 nNewEndAngle) : SdrRectObj(rSdrModel, rRect) { Degree100 nAngleDif=nNewEndAngle-nNewStartAngle; nStartAngle=NormAngle36000(nNewStartAngle); nEndAngle=NormAngle36000(nNewEndAngle); if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle meCircleKind=eNewKind; m_bClosedObj=eNewKind!=SdrCircKind::Arc; } SdrCircObj::~SdrCircObj() { } void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const { bool bCanConv=!HasText() || ImpCanConvTextToCurve(); rInfo.bEdgeRadiusAllowed = false; rInfo.bCanConvToPath=bCanConv; rInfo.bCanConvToPoly=bCanConv; rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary()); } SdrObjKind SdrCircObj::GetObjIdentifier() const { switch (meCircleKind) { case SdrCircKind::Full: return SdrObjKind::CircleOrEllipse; case SdrCircKind::Section: return SdrObjKind::CircleSection; case SdrCircKind::Cut: return SdrObjKind::CircleCut; case SdrCircKind::Arc: return SdrObjKind::CircleArc; default: assert(false); } return SdrObjKind::CircleOrEllipse; } bool SdrCircObj::PaintNeedsXPolyCirc() const { // XPoly is necessary for all rotated ellipse objects, circle and // ellipse segments. // If not WIN, then (for now) also for circle/ellipse segments and circle/ // ellipse arcs (for precision) bool bNeed = maGeo.m_nRotationAngle || maGeo.m_nShearAngle || meCircleKind == SdrCircKind::Cut; // If not WIN, then for everything except full circle (for now!) if (meCircleKind!=SdrCircKind::Full) bNeed = true; const SfxItemSet& rSet = GetObjectItemSet(); if(!bNeed) { // XPoly is necessary for everything that isn't LineSolid or LineNone drawing::LineStyle eLine = rSet.Get(XATTR_LINESTYLE).GetValue(); bNeed = eLine != drawing::LineStyle_NONE && eLine != drawing::LineStyle_SOLID; // XPoly is necessary for thick lines if(!bNeed && eLine != drawing::LineStyle_NONE) bNeed = rSet.Get(XATTR_LINEWIDTH).GetValue() != 0; // XPoly is necessary for circle arcs with line ends if(!bNeed && meCircleKind == SdrCircKind::Arc) { // start of the line is here if StartPolygon, StartWidth!=0 bNeed=rSet.Get(XATTR_LINESTART).GetLineStartValue().count() != 0 && rSet.Get(XATTR_LINESTARTWIDTH).GetValue() != 0; if(!bNeed) { // end of the line is here if EndPolygon, EndWidth!=0 bNeed = rSet.Get(XATTR_LINEEND).GetLineEndValue().count() != 0 && rSet.Get(XATTR_LINEENDWIDTH).GetValue() != 0; } } } // XPoly is necessary if Fill !=None and !=Solid if(!bNeed && meCircleKind != SdrCircKind::Arc) { drawing::FillStyle eFill=rSet.Get(XATTR_FILLSTYLE).GetValue(); bNeed = eFill != drawing::FillStyle_NONE && eFill != drawing::FillStyle_SOLID; } if(!bNeed && meCircleKind != SdrCircKind::Full && nStartAngle == nEndAngle) bNeed = true; // otherwise we're drawing a full circle return bNeed; } basegfx::B2DPolygon SdrCircObj::ImpCalcXPolyCirc(const SdrCircKind eCircleKind, const tools::Rectangle& rRect1, Degree100 nStart, Degree100 nEnd) const { const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRect1); basegfx::B2DPolygon aCircPolygon; if(SdrCircKind::Full == eCircleKind) { // create full circle. Do not use createPolygonFromEllipse; it's necessary // to get the start point to the bottom of the circle to keep compatible to // old geometry creation aCircPolygon = basegfx::utils::createPolygonFromUnitCircle(1); // needs own scaling and translation from unit circle to target size (same as // would be in createPolygonFromEllipse) const basegfx::B2DPoint aCenter(aRange.getCenter()); const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix( aRange.getWidth() / 2.0, aRange.getHeight() / 2.0, aCenter.getX(), aCenter.getY())); aCircPolygon.transform(aMatrix); } else { // mirror start, end for geometry creation since model coordinate system is mirrored in Y const double fStart(toRadians((36000_deg100 - nEnd) % 36000_deg100)); const double fEnd(toRadians((36000_deg100 - nStart) % 36000_deg100)); // create circle segment. This is not closed by default aCircPolygon = basegfx::utils::createPolygonFromEllipseSegment( aRange.getCenter(), aRange.getWidth() / 2.0, aRange.getHeight() / 2.0, fStart, fEnd); // check closing states const bool bCloseSegment(SdrCircKind::Arc != eCircleKind); const bool bCloseUsingCenter(SdrCircKind::Section == eCircleKind); if(bCloseSegment) { if(bCloseUsingCenter) { // add center point at start (for historical reasons) basegfx::B2DPolygon aSector; aSector.append(aRange.getCenter()); aSector.append(aCircPolygon); aCircPolygon = std::move(aSector); } // close aCircPolygon.setClosed(true); } } // #i76950# if (maGeo.m_nShearAngle || maGeo.m_nRotationAngle) { // translate top left to (0,0) const basegfx::B2DPoint aTopLeft(aRange.getMinimum()); basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix( -aTopLeft.getX(), -aTopLeft.getY())); // shear, rotate and back to top left (if needed) aMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix( -maGeo.mfTanShearAngle, maGeo.m_nRotationAngle ? toRadians(36000_deg100 - maGeo.m_nRotationAngle) : 0.0, aTopLeft) * aMatrix; // apply transformation aCircPolygon.transform(aMatrix); } return aCircPolygon; } void SdrCircObj::RecalcXPoly() { basegfx::B2DPolygon aPolyCirc(ImpCalcXPolyCirc(meCircleKind, getRectangle(), nStartAngle, nEndAngle)); mpXPoly = XPolygon(aPolyCirc); } OUString SdrCircObj::TakeObjNameSingul() const { TranslateId pID=STR_ObjNameSingulCIRC; if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo.m_nShearAngle == 0_deg100) { switch (meCircleKind) { case SdrCircKind::Full: pID=STR_ObjNameSingulCIRC; break; case SdrCircKind::Section: pID=STR_ObjNameSingulSECT; break; case SdrCircKind::Arc: pID=STR_ObjNameSingulCARC; break; case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUT; break; default: break; } } else { switch (meCircleKind) { case SdrCircKind::Full: pID=STR_ObjNameSingulCIRCE; break; case SdrCircKind::Section: pID=STR_ObjNameSingulSECTE; break; case SdrCircKind::Arc: pID=STR_ObjNameSingulCARCE; break; case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUTE; break; default: break; } } OUString sName(SvxResId(pID)); OUString aName(GetName()); if (!aName.isEmpty()) sName += " '" + aName + "'"; return sName; } OUString SdrCircObj::TakeObjNamePlural() const { TranslateId pID=STR_ObjNamePluralCIRC; if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo.m_nShearAngle == 0_deg100) { switch (meCircleKind) { case SdrCircKind::Full: pID=STR_ObjNamePluralCIRC; break; case SdrCircKind::Section: pID=STR_ObjNamePluralSECT; break; case SdrCircKind::Arc: pID=STR_ObjNamePluralCARC; break; case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUT; break; default: break; } } else { switch (meCircleKind) { case SdrCircKind::Full: pID=STR_ObjNamePluralCIRCE; break; case SdrCircKind::Section: pID=STR_ObjNamePluralSECTE; break; case SdrCircKind::Arc: pID=STR_ObjNamePluralCARCE; break; case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUTE; break; default: break; } } return SvxResId(pID); } rtl::Reference SdrCircObj::CloneSdrObject(SdrModel& rTargetModel) const { return new SdrCircObj(rTargetModel, *this); } basegfx::B2DPolyPolygon SdrCircObj::TakeXorPoly() const { const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, getRectangle(), nStartAngle, nEndAngle)); return basegfx::B2DPolyPolygon(aCircPolygon); } namespace { struct ImpCircUser : public SdrDragStatUserData { tools::Rectangle aR; Point aCenter; Point aP1; tools::Long nHgt; tools::Long nWdt; Degree100 nStart; Degree100 nEnd; public: ImpCircUser() : nHgt(0), nWdt(0), nStart(0), nEnd(0) {} void SetCreateParams(SdrDragStat const & rStat); }; } sal_uInt32 SdrCircObj::GetHdlCount() const { if(SdrCircKind::Full != meCircleKind) { return 10; } else { return 8; } } void SdrCircObj::AddToHdlList(SdrHdlList& rHdlList) const { for (sal_uInt32 nHdlNum=(SdrCircKind::Full==meCircleKind)?2:0; nHdlNum<=9; ++nHdlNum) { Point aPnt; SdrHdlKind eLocalKind(SdrHdlKind::Move); sal_uInt32 nPNum(0); tools::Rectangle aRectangle = getRectangle(); switch (nHdlNum) { case 0: aPnt = GetAnglePnt(aRectangle, nStartAngle); eLocalKind = SdrHdlKind::Circle; nPNum = 1; break; case 1: aPnt = GetAnglePnt(aRectangle, nEndAngle); eLocalKind = SdrHdlKind::Circle; nPNum = 2; break; case 2: aPnt = aRectangle.TopLeft(); eLocalKind = SdrHdlKind::UpperLeft; break; case 3: aPnt = aRectangle.TopCenter(); eLocalKind = SdrHdlKind::Upper; break; case 4: aPnt = aRectangle.TopRight(); eLocalKind = SdrHdlKind::UpperRight; break; case 5: aPnt = aRectangle.LeftCenter(); eLocalKind = SdrHdlKind::Left; break; case 6: aPnt = aRectangle.RightCenter(); eLocalKind = SdrHdlKind::Right; break; case 7: aPnt = aRectangle.BottomLeft(); eLocalKind = SdrHdlKind::LowerLeft; break; case 8: aPnt = aRectangle.BottomCenter(); eLocalKind = SdrHdlKind::Lower; break; case 9: aPnt = aRectangle.BottomRight(); eLocalKind = SdrHdlKind::LowerRight; break; } if (maGeo.m_nShearAngle) { ShearPoint(aPnt, aRectangle.TopLeft(), maGeo.mfTanShearAngle); } if (maGeo.m_nRotationAngle) { RotatePoint(aPnt, aRectangle.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); } std::unique_ptr pH(new SdrHdl(aPnt,eLocalKind)); pH->SetPointNum(nPNum); pH->SetObj(const_cast(this)); pH->SetRotationAngle(maGeo.m_nRotationAngle); rHdlList.AddHdl(std::move(pH)); } } bool SdrCircObj::hasSpecialDrag() const { return true; } bool SdrCircObj::beginSpecialDrag(SdrDragStat& rDrag) const { const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind()); if(bAngle) { if(1 == rDrag.GetHdl()->GetPointNum() || 2 == rDrag.GetHdl()->GetPointNum()) { rDrag.SetNoSnap(); } return true; } return SdrTextObj::beginSpecialDrag(rDrag); } bool SdrCircObj::applySpecialDrag(SdrDragStat& rDrag) { const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind()); if(bAngle) { Point aPt(rDrag.GetNow()); if (maGeo.m_nRotationAngle) RotatePoint(aPt, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); if (maGeo.m_nShearAngle) ShearPoint(aPt, getRectangle().TopLeft(), -maGeo.mfTanShearAngle); aPt -= getRectangle().Center(); tools::Long nWdt = getRectangle().Right() - getRectangle().Left(); tools::Long nHgt = getRectangle().Bottom() - getRectangle().Top(); if(nWdt>=nHgt) { aPt.setY(BigMulDiv(aPt.Y(),nWdt,nHgt) ); } else { aPt.setX(BigMulDiv(aPt.X(),nHgt,nWdt) ); } Degree100 nAngle=NormAngle36000(GetAngle(aPt)); if (rDrag.GetView() && rDrag.GetView()->IsAngleSnapEnabled()) { Degree100 nSA=rDrag.GetView()->GetSnapAngle(); if (nSA) { nAngle+=nSA/2_deg100; nAngle/=nSA; nAngle*=nSA; nAngle=NormAngle36000(nAngle); } } if(1 == rDrag.GetHdl()->GetPointNum()) { nStartAngle = nAngle; } else if(2 == rDrag.GetHdl()->GetPointNum()) { nEndAngle = nAngle; } SetBoundAndSnapRectsDirty(); SetXPolyDirty(); ImpSetCircInfoToAttr(); SetChanged(); return true; } else { return SdrTextObj::applySpecialDrag(rDrag); } } OUString SdrCircObj::getSpecialDragComment(const SdrDragStat& rDrag) const { const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj()); if(bCreateComment) { OUStringBuffer aBuf(ImpGetDescriptionStr(STR_ViewCreateObj)); const sal_uInt32 nPointCount(rDrag.GetPointCount()); if(SdrCircKind::Full != meCircleKind && nPointCount > 2) { const ImpCircUser* pU = static_cast(rDrag.GetUser()); Degree100 nAngle; aBuf.append(" ("); if(3 == nPointCount) { nAngle = pU->nStart; } else { nAngle = pU->nEnd; } aBuf.append(SdrModel::GetAngleString(nAngle)); aBuf.append(')'); } return aBuf.makeStringAndClear(); } else { const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind()); if(bAngle) { const Degree100 nAngle(1 == rDrag.GetHdl()->GetPointNum() ? nStartAngle : nEndAngle); return ImpGetDescriptionStr(STR_DragCircAngle) + " (" + SdrModel::GetAngleString(nAngle) + ")"; } else { return SdrTextObj::getSpecialDragComment(rDrag); } } } void ImpCircUser::SetCreateParams(SdrDragStat const & rStat) { rStat.TakeCreateRect(aR); aR.Normalize(); aCenter=aR.Center(); nWdt=aR.Right()-aR.Left(); nHgt=aR.Bottom()-aR.Top(); nStart=0_deg100; nEnd=36000_deg100; if (rStat.GetPointCount()>2) { Point aP(rStat.GetPoint(2)-aCenter); if (nWdt==0) aP.setX(0 ); if (nHgt==0) aP.setY(0 ); if (nWdt>=nHgt) { if (nHgt!=0) aP.setY(aP.Y()*nWdt/nHgt ); } else { if (nWdt!=0) aP.setX(aP.X()*nHgt/nWdt ); } nStart=NormAngle36000(GetAngle(aP)); if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) { Degree100 nSA=rStat.GetView()->GetSnapAngle(); if (nSA) { // angle snapping nStart+=nSA/2_deg100; nStart/=nSA; nStart*=nSA; nStart=NormAngle36000(nStart); } } aP1 = GetAnglePnt(aR,nStart); nEnd=nStart; } else aP1=aCenter; if (rStat.GetPointCount()<=3) return; Point aP(rStat.GetPoint(3)-aCenter); if (nWdt>=nHgt) { aP.setY(BigMulDiv(aP.Y(),nWdt,nHgt) ); } else { aP.setX(BigMulDiv(aP.X(),nHgt,nWdt) ); } nEnd=NormAngle36000(GetAngle(aP)); if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) { Degree100 nSA=rStat.GetView()->GetSnapAngle(); if (nSA) { // angle snapping nEnd+=nSA/2_deg100; nEnd/=nSA; nEnd*=nSA; nEnd=NormAngle36000(nEnd); } } } void SdrCircObj::ImpSetCreateParams(SdrDragStat& rStat) { ImpCircUser* pU=static_cast(rStat.GetUser()); if (pU==nullptr) { pU=new ImpCircUser; rStat.SetUser(std::unique_ptr(pU)); } pU->SetCreateParams(rStat); } bool SdrCircObj::BegCreate(SdrDragStat& rStat) { rStat.SetOrtho4Possible(); tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow()); aRect1.Normalize(); rStat.SetActionRect(aRect1); setRectangle(aRect1); ImpSetCreateParams(rStat); return true; } bool SdrCircObj::MovCreate(SdrDragStat& rStat) { ImpSetCreateParams(rStat); ImpCircUser* pU=static_cast(rStat.GetUser()); rStat.SetActionRect(pU->aR); setRectangle(pU->aR); // for ObjName ImpJustifyRect(maRectangle); nStartAngle=pU->nStart; nEndAngle=pU->nEnd; SetBoundRectDirty(); m_bSnapRectDirty=true; SetXPolyDirty(); // #i103058# push current angle settings to ItemSet to // allow FullDrag visualisation if(rStat.GetPointCount() >= 4) { ImpSetCircInfoToAttr(); } return true; } bool SdrCircObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd) { ImpSetCreateParams(rStat); ImpCircUser* pU=static_cast(rStat.GetUser()); bool bRet = false; if (eCmd==SdrCreateCmd::ForceEnd && rStat.GetPointCount()<4) meCircleKind=SdrCircKind::Full; if (meCircleKind==SdrCircKind::Full) { bRet=rStat.GetPointCount()>=2; if (bRet) { tools::Rectangle aRectangle(pU->aR); ImpJustifyRect(aRectangle); setRectangle(aRectangle); } } else { rStat.SetNoSnap(rStat.GetPointCount()>=2); rStat.SetOrtho4Possible(rStat.GetPointCount()<2); bRet=rStat.GetPointCount()>=4; if (bRet) { tools::Rectangle aRectangle(pU->aR); ImpJustifyRect(aRectangle); setRectangle(aRectangle); nStartAngle=pU->nStart; nEndAngle=pU->nEnd; } } m_bClosedObj=meCircleKind!=SdrCircKind::Arc; SetBoundAndSnapRectsDirty(); SetXPolyDirty(); ImpSetCircInfoToAttr(); if (bRet) rStat.SetUser(nullptr); return bRet; } void SdrCircObj::BrkCreate(SdrDragStat& rStat) { rStat.SetUser(nullptr); } bool SdrCircObj::BckCreate(SdrDragStat& rStat) { rStat.SetNoSnap(rStat.GetPointCount()>=3); rStat.SetOrtho4Possible(rStat.GetPointCount()<3); return meCircleKind!=SdrCircKind::Full; } basegfx::B2DPolyPolygon SdrCircObj::TakeCreatePoly(const SdrDragStat& rDrag) const { const ImpCircUser* pU = static_cast(rDrag.GetUser()); if(rDrag.GetPointCount() < 4) { // force to OBJ_CIRC to get full visualisation basegfx::B2DPolyPolygon aRetval(ImpCalcXPolyCirc(SdrCircKind::Full, pU->aR, pU->nStart, pU->nEnd)); if(3 == rDrag.GetPointCount()) { // add edge to first point on ellipse basegfx::B2DPolygon aNew; aNew.append(basegfx::B2DPoint(pU->aCenter.X(), pU->aCenter.Y())); aNew.append(basegfx::B2DPoint(pU->aP1.X(), pU->aP1.Y())); aRetval.append(aNew); } return aRetval; } else { return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind, pU->aR, pU->nStart, pU->nEnd)); } } PointerStyle SdrCircObj::GetCreatePointer() const { switch (meCircleKind) { case SdrCircKind::Full: return PointerStyle::DrawEllipse; case SdrCircKind::Section: return PointerStyle::DrawPie; case SdrCircKind::Arc: return PointerStyle::DrawArc; case SdrCircKind::Cut: return PointerStyle::DrawCircleCut; default: break; } // switch return PointerStyle::Cross; } void SdrCircObj::NbcMove(const Size& aSize) { moveRectangle(aSize.Width(), aSize.Height()); moveOutRectangle(aSize.Width(), aSize.Height()); maSnapRect.Move(aSize); SetXPolyDirty(); SetBoundAndSnapRectsDirty(true); } void SdrCircObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) { Degree100 nAngle0 = maGeo.m_nRotationAngle; bool bNoShearRota = (maGeo.m_nRotationAngle == 0_deg100 && maGeo.m_nShearAngle == 0_deg100); SdrTextObj::NbcResize(rRef,xFact,yFact); bNoShearRota |= (maGeo.m_nRotationAngle == 0_deg100 && maGeo.m_nShearAngle == 0_deg100); if (meCircleKind!=SdrCircKind::Full) { bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0); bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0); if (bXMirr || bYMirr) { // At bXMirr!=bYMirr we should actually swap both line ends. // That, however, is pretty bad (because of forced "hard" formatting). // Alternatively, we could implement a bMirrored flag (maybe even // a more general one, e. g. for mirrored text, ...). Degree100 nS0=nStartAngle; Degree100 nE0=nEndAngle; if (bNoShearRota) { // the RectObj already mirrors at VMirror because of a 180deg rotation if (! (bXMirr && bYMirr)) { Degree100 nTmp=nS0; nS0=18000_deg100-nE0; nE0=18000_deg100-nTmp; } } else { // mirror contorted ellipses if (bXMirr!=bYMirr) { nS0+=nAngle0; nE0+=nAngle0; if (bXMirr) { Degree100 nTmp=nS0; nS0=18000_deg100-nE0; nE0=18000_deg100-nTmp; } if (bYMirr) { Degree100 nTmp=nS0; nS0=-nE0; nE0=-nTmp; } nS0 -= maGeo.m_nRotationAngle; nE0 -= maGeo.m_nRotationAngle; } } Degree100 nAngleDif=nE0-nS0; nStartAngle=NormAngle36000(nS0); nEndAngle =NormAngle36000(nE0); if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle } } SetXPolyDirty(); ImpSetCircInfoToAttr(); } void SdrCircObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear) { SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear); SetXPolyDirty(); ImpSetCircInfoToAttr(); } void SdrCircObj::NbcMirror(const Point& rRef1, const Point& rRef2) { bool bFreeMirr=meCircleKind!=SdrCircKind::Full; Point aTmpPt1; Point aTmpPt2; if (bFreeMirr) { // some preparations for using an arbitrary axis of reflection Point aCenter(getRectangle().Center()); tools::Long nWdt = getRectangle().GetWidth() - 1; tools::Long nHgt = getRectangle().GetHeight() - 1; tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2; // starting point double a = toRadians(nStartAngle); aTmpPt1 = Point(basegfx::fround(cos(a) * nMaxRad), basegfx::fround(-sin(a) * nMaxRad)); if (nWdt==0) aTmpPt1.setX(0 ); if (nHgt==0) aTmpPt1.setY(0 ); aTmpPt1+=aCenter; // finishing point a = toRadians(nEndAngle); aTmpPt2 = Point(basegfx::fround(cos(a) * nMaxRad), basegfx::fround(-sin(a) * nMaxRad)); if (nWdt==0) aTmpPt2.setX(0 ); if (nHgt==0) aTmpPt2.setY(0 ); aTmpPt2+=aCenter; if (maGeo.m_nRotationAngle) { RotatePoint(aTmpPt1, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); RotatePoint(aTmpPt2, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); } if (maGeo.m_nShearAngle) { ShearPoint(aTmpPt1, getRectangle().TopLeft(), maGeo.mfTanShearAngle); ShearPoint(aTmpPt2, getRectangle().TopLeft(), maGeo.mfTanShearAngle); } } SdrTextObj::NbcMirror(rRef1,rRef2); if (meCircleKind!=SdrCircKind::Full) { // adapt starting and finishing angle MirrorPoint(aTmpPt1,rRef1,rRef2); MirrorPoint(aTmpPt2,rRef1,rRef2); // unrotate: if (maGeo.m_nRotationAngle) { RotatePoint(aTmpPt1, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion RotatePoint(aTmpPt2, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion } // unshear: if (maGeo.m_nShearAngle) { ShearPoint(aTmpPt1, getRectangle().TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion ShearPoint(aTmpPt2, getRectangle().TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion } Point aCenter(getRectangle().Center()); aTmpPt1-=aCenter; aTmpPt2-=aCenter; // because it's mirrored, the angles are swapped, too nStartAngle=GetAngle(aTmpPt2); nEndAngle =GetAngle(aTmpPt1); Degree100 nAngleDif=nEndAngle-nStartAngle; nStartAngle=NormAngle36000(nStartAngle); nEndAngle =NormAngle36000(nEndAngle); if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle } SetXPolyDirty(); ImpSetCircInfoToAttr(); } std::unique_ptr SdrCircObj::NewGeoData() const { return std::make_unique(); } void SdrCircObj::SaveGeoData(SdrObjGeoData& rGeo) const { SdrRectObj::SaveGeoData(rGeo); SdrCircObjGeoData& rCGeo=static_cast(rGeo); rCGeo.nStartAngle=nStartAngle; rCGeo.nEndAngle =nEndAngle; } void SdrCircObj::RestoreGeoData(const SdrObjGeoData& rGeo) { SdrRectObj::RestoreGeoData(rGeo); const SdrCircObjGeoData& rCGeo=static_cast(rGeo); nStartAngle=rCGeo.nStartAngle; nEndAngle =rCGeo.nEndAngle; SetXPolyDirty(); ImpSetCircInfoToAttr(); } static void Union(tools::Rectangle& rR, const Point& rP) { if (rP.X()rR.Right ()) rR.SetRight(rP.X() ); if (rP.Y()rR.Bottom()) rR.SetBottom(rP.Y() ); } void SdrCircObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const { rRect = getRectangle(); if (meCircleKind!=SdrCircKind::Full) { const Point aPntStart(GetAnglePnt(getRectangle(), nStartAngle)); const Point aPntEnd(GetAnglePnt(getRectangle(), nEndAngle)); Degree100 a=nStartAngle; Degree100 e=nEndAngle; rRect.SetLeft(getRectangle().Right() ); rRect.SetRight(getRectangle().Left() ); rRect.SetTop(getRectangle().Bottom() ); rRect.SetBottom(getRectangle().Top() ); Union(rRect,aPntStart); Union(rRect,aPntEnd); if ((a<=18000_deg100 && e>=18000_deg100) || (a>e && (a<=18000_deg100 || e>=18000_deg100))) { Union(rRect, getRectangle().LeftCenter()); } if ((a<=27000_deg100 && e>=27000_deg100) || (a>e && (a<=27000_deg100 || e>=27000_deg100))) { Union(rRect, getRectangle().BottomCenter()); } if (a>e) { Union(rRect, getRectangle().RightCenter()); } if ((a<=9000_deg100 && e>=9000_deg100) || (a>e && (a<=9000_deg100 || e>=9000_deg100))) { Union(rRect, getRectangle().TopCenter()); } if (meCircleKind==SdrCircKind::Section) { Union(rRect, getRectangle().Center()); } if (maGeo.m_nRotationAngle) { Point aDst(rRect.TopLeft()); aDst -= getRectangle().TopLeft(); Point aDst0(aDst); RotatePoint(aDst,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); aDst-=aDst0; rRect.Move(aDst.X(),aDst.Y()); } } if (maGeo.m_nShearAngle==0_deg100) return; tools::Long nDst = basegfx::fround((rRect.Bottom() - rRect.Top()) * maGeo.mfTanShearAngle); if (maGeo.m_nShearAngle > 0_deg100) { Point aRef(rRect.TopLeft()); rRect.AdjustLeft( -nDst ); Point aTmpPt(rRect.TopLeft()); RotatePoint(aTmpPt, aRef, maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); aTmpPt-=rRect.TopLeft(); rRect.Move(aTmpPt.X(),aTmpPt.Y()); } else { rRect.AdjustRight( -nDst ); } } void SdrCircObj::RecalcSnapRect() { if (PaintNeedsXPolyCirc()) { maSnapRect=GetXPoly().GetBoundRect(); } else { TakeUnrotatedSnapRect(maSnapRect); } } void SdrCircObj::NbcSetSnapRect(const tools::Rectangle& rRect) { if (maGeo.m_nRotationAngle || maGeo.m_nShearAngle || meCircleKind != SdrCircKind::Full) { tools::Rectangle aSR0(GetSnapRect()); tools::Long nWdt0=aSR0.Right()-aSR0.Left(); tools::Long nHgt0=aSR0.Bottom()-aSR0.Top(); tools::Long nWdt1=rRect.Right()-rRect.Left(); tools::Long nHgt1=rRect.Bottom()-rRect.Top(); NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0)); NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top())); } else { setRectangle(rRect); ImpJustifyRect(maRectangle); } SetBoundAndSnapRectsDirty(); SetXPolyDirty(); ImpSetCircInfoToAttr(); } sal_uInt32 SdrCircObj::GetSnapPointCount() const { if (meCircleKind==SdrCircKind::Full) { return 1; } else { return 3; } } Point SdrCircObj::GetSnapPoint(sal_uInt32 i) const { switch (i) { case 1 : return GetAnglePnt(getRectangle(), nStartAngle); case 2 : return GetAnglePnt(getRectangle(), nEndAngle); default: return getRectangle().Center(); } } void SdrCircObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) { SetXPolyDirty(); SdrRectObj::Notify(rBC,rHint); ImpSetAttrToCircInfo(); } void SdrCircObj::ImpSetAttrToCircInfo() { const SfxItemSet& rSet = GetObjectItemSet(); SdrCircKind eNewKind = rSet.Get(SDRATTR_CIRCKIND).GetValue(); Degree100 nNewStart = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue(); Degree100 nNewEnd = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue(); bool bKindChg = meCircleKind != eNewKind; bool bAngleChg = nNewStart != nStartAngle || nNewEnd != nEndAngle; if(bKindChg || bAngleChg) { meCircleKind = eNewKind; nStartAngle = nNewStart; nEndAngle = nNewEnd; if(bKindChg || (meCircleKind != SdrCircKind::Full && bAngleChg)) { SetXPolyDirty(); SetBoundAndSnapRectsDirty(); } } } void SdrCircObj::ImpSetCircInfoToAttr() { const SfxItemSet& rSet = GetObjectItemSet(); SdrCircKind eOldKindA = rSet.Get(SDRATTR_CIRCKIND).GetValue(); Degree100 nOldStartAngle = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue(); Degree100 nOldEndAngle = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue(); if(meCircleKind == eOldKindA && nStartAngle == nOldStartAngle && nEndAngle == nOldEndAngle) return; // since SetItem() implicitly calls ImpSetAttrToCircInfo() // setting the item directly is necessary here. if(meCircleKind != eOldKindA) { GetProperties().SetObjectItemDirect(SdrCircKindItem(meCircleKind)); } if(nStartAngle != nOldStartAngle) { GetProperties().SetObjectItemDirect(makeSdrCircStartAngleItem(nStartAngle)); } if(nEndAngle != nOldEndAngle) { GetProperties().SetObjectItemDirect(makeSdrCircEndAngleItem(nEndAngle)); } SetXPolyDirty(); ImpSetAttrToCircInfo(); } rtl::Reference SdrCircObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const { const bool bFill(meCircleKind != SdrCircKind::Arc); const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, getRectangle(), nStartAngle, nEndAngle)); rtl::Reference pRet = ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon), bFill, bBezier); if(bAddText) { pRet = ImpConvertAddText(std::move(pRet), bBezier); } return pRet; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */