/* -*- 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 using namespace com::sun::star; // BaseProperties section std::unique_ptr SdrRectObj::CreateObjectSpecificProperties() { return std::make_unique(*this); } // DrawContact section std::unique_ptr SdrRectObj::CreateObjectSpecificViewContact() { return std::make_unique(*this); } SdrRectObj::SdrRectObj(SdrModel& rSdrModel) : SdrTextObj(rSdrModel) { m_bClosedObj=true; } SdrRectObj::SdrRectObj(SdrModel& rSdrModel, SdrRectObj const & rSource) : SdrTextObj(rSdrModel, rSource) { m_bClosedObj=true; mpXPoly = rSource.mpXPoly; } SdrRectObj::SdrRectObj( SdrModel& rSdrModel, const tools::Rectangle& rRect) : SdrTextObj(rSdrModel, rRect) { m_bClosedObj=true; } SdrRectObj::SdrRectObj( SdrModel& rSdrModel, SdrObjKind eNewTextKind) : SdrTextObj(rSdrModel, eNewTextKind) { DBG_ASSERT(meTextKind == SdrObjKind::Text || meTextKind == SdrObjKind::OutlineText || meTextKind == SdrObjKind::TitleText, "SdrRectObj::SdrRectObj(SdrObjKind) can only be applied to text frames."); m_bClosedObj=true; } SdrRectObj::SdrRectObj( SdrModel& rSdrModel, SdrObjKind eNewTextKind, const tools::Rectangle& rRect) : SdrTextObj(rSdrModel, eNewTextKind, rRect) { DBG_ASSERT(meTextKind == SdrObjKind::Text || meTextKind == SdrObjKind::OutlineText || meTextKind == SdrObjKind::TitleText, "SdrRectObj::SdrRectObj(SdrObjKind,...) can only be applied to text frames."); m_bClosedObj=true; } SdrRectObj::~SdrRectObj() { } void SdrRectObj::SetXPolyDirty() { mpXPoly.reset(); } XPolygon SdrRectObj::ImpCalcXPoly(const tools::Rectangle& rRect1, tools::Long nRad1) const { XPolygon aXPoly(rRect1,nRad1,nRad1); const sal_uInt16 nPointCnt(aXPoly.GetPointCount()); XPolygon aNewPoly(nPointCnt+1); sal_uInt16 nShift=nPointCnt-2; if (nRad1!=0) nShift=nPointCnt-5; sal_uInt16 j=nShift; for (sal_uInt16 i=1; i=nPointCnt) j=1; } aNewPoly[0]=rRect1.BottomCenter(); aNewPoly[nPointCnt]=aNewPoly[0]; aXPoly=aNewPoly; // these angles always relate to the top left corner of aRect if (maGeo.m_nShearAngle) ShearXPoly(aXPoly, getRectangle().TopLeft(), maGeo.mfTanShearAngle); if (maGeo.m_nRotationAngle) RotateXPoly(aXPoly, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); return aXPoly; } void SdrRectObj::RecalcXPoly() { mpXPoly = ImpCalcXPoly(getRectangle(), GetEckenradius()); } const XPolygon& SdrRectObj::GetXPoly() const { if(!mpXPoly) { const_cast(this)->RecalcXPoly(); } return *mpXPoly; } void SdrRectObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const { bool bNoTextFrame=!IsTextFrame(); rInfo.bResizeFreeAllowed=bNoTextFrame || ((maGeo.m_nRotationAngle.get() % 9000) == 0); rInfo.bResizePropAllowed=true; rInfo.bRotateFreeAllowed=true; rInfo.bRotate90Allowed =true; rInfo.bMirrorFreeAllowed=bNoTextFrame; rInfo.bMirror45Allowed =bNoTextFrame; rInfo.bMirror90Allowed =bNoTextFrame; // allow transparency rInfo.bTransparenceAllowed = true; rInfo.bShearAllowed =bNoTextFrame; rInfo.bEdgeRadiusAllowed=true; bool bCanConv=!HasText() || ImpCanConvTextToCurve(); if (bCanConv && !bNoTextFrame && !HasText()) { bCanConv=HasFill() || HasLine(); } rInfo.bCanConvToPath =bCanConv; rInfo.bCanConvToPoly =bCanConv; rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary()); } SdrObjKind SdrRectObj::GetObjIdentifier() const { if (IsTextFrame()) return meTextKind; else return SdrObjKind::Rectangle; } void SdrRectObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const { rRect = getRectangle(); if (maGeo.m_nShearAngle==0_deg100) return; tools::Long nDst=basegfx::fround((getRectangle().Bottom()-getRectangle().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 ); } } OUString SdrRectObj::TakeObjNameSingul() const { if (IsTextFrame()) { return SdrTextObj::TakeObjNameSingul(); } bool bRounded = GetEckenradius() != 0; // rounded down TranslateId pResId = bRounded ? STR_ObjNameSingulRECTRND : STR_ObjNameSingulRECT; if (maGeo.m_nShearAngle) { pResId = bRounded ? STR_ObjNameSingulPARALRND : STR_ObjNameSingulPARAL; // parallelogram or, maybe, rhombus } else if (getRectangle().GetWidth() == getRectangle().GetHeight()) { pResId = bRounded ? STR_ObjNameSingulQUADRND : STR_ObjNameSingulQUAD; // square } OUString sName(SvxResId(pResId)); OUString aName(GetName()); if (!aName.isEmpty()) sName += " '" + aName + "'"; return sName; } OUString SdrRectObj::TakeObjNamePlural() const { if (IsTextFrame()) { return SdrTextObj::TakeObjNamePlural(); } bool bRounded = GetEckenradius() != 0; // rounded down TranslateId pResId = bRounded ? STR_ObjNamePluralRECTRND : STR_ObjNamePluralRECT; if (maGeo.m_nShearAngle) { pResId = bRounded ? STR_ObjNamePluralPARALRND : STR_ObjNamePluralPARAL; // parallelogram or rhombus } else if (getRectangle().GetWidth() == getRectangle().GetHeight()) { pResId = bRounded ? STR_ObjNamePluralQUADRND : STR_ObjNamePluralQUAD; // square } return SvxResId(pResId); } rtl::Reference SdrRectObj::CloneSdrObject(SdrModel& rTargetModel) const { return new SdrRectObj(rTargetModel, *this); } basegfx::B2DPolyPolygon SdrRectObj::TakeXorPoly() const { XPolyPolygon aXPP; aXPP.Insert(ImpCalcXPoly(getRectangle(), GetEckenradius())); return aXPP.getB2DPolyPolygon(); } void SdrRectObj::RecalcSnapRect() { tools::Long nEckRad=GetEckenradius(); if ((maGeo.m_nRotationAngle || maGeo.m_nShearAngle) && nEckRad!=0) { maSnapRect=GetXPoly().GetBoundRect(); } else { SdrTextObj::RecalcSnapRect(); } } void SdrRectObj::NbcSetSnapRect(const tools::Rectangle& rRect) { SdrTextObj::NbcSetSnapRect(rRect); SetXPolyDirty(); } void SdrRectObj::NbcSetLogicRect(const tools::Rectangle& rRect) { SdrTextObj::NbcSetLogicRect(rRect); SetXPolyDirty(); } sal_uInt32 SdrRectObj::GetHdlCount() const { return IsTextFrame() ? 10 : 9; } void SdrRectObj::AddToHdlList(SdrHdlList& rHdlList) const { // A text box has an additional (pseudo-)handle for the blinking frame. if(IsTextFrame()) { OSL_ENSURE(!IsTextEditActive(), "Do not use an ImpTextframeHdl for highlighting text in active text edit, this will collide with EditEngine paints (!)"); std::unique_ptr pH(new ImpTextframeHdl(getRectangle())); pH->SetObj(const_cast(this)); pH->SetRotationAngle(maGeo.m_nRotationAngle); rHdlList.AddHdl(std::move(pH)); } for(sal_Int32 nHdlNum = 1; nHdlNum <= 9; ++nHdlNum) { Point aPnt; SdrHdlKind eKind = SdrHdlKind::Move; auto const& rRectangle = getRectangle(); switch(nHdlNum) { case 1: // Handle for changing the corner radius { tools::Long a = GetEckenradius(); tools::Long b = std::max(rRectangle.GetWidth(), rRectangle.GetHeight())/2; // rounded up, because GetWidth() adds 1 if (a>b) a=b; if (a<0) a=0; aPnt = rRectangle.TopLeft(); aPnt.AdjustX(a ); eKind = SdrHdlKind::Circle; break; } case 2: aPnt = rRectangle.TopLeft(); eKind = SdrHdlKind::UpperLeft; break; case 3: aPnt = rRectangle.TopCenter(); eKind = SdrHdlKind::Upper; break; case 4: aPnt = rRectangle.TopRight(); eKind = SdrHdlKind::UpperRight; break; case 5: aPnt = rRectangle.LeftCenter(); eKind = SdrHdlKind::Left ; break; case 6: aPnt = rRectangle.RightCenter(); eKind = SdrHdlKind::Right; break; case 7: aPnt = rRectangle.BottomLeft(); eKind = SdrHdlKind::LowerLeft; break; case 8: aPnt = rRectangle.BottomCenter(); eKind = SdrHdlKind::Lower; break; case 9: aPnt = rRectangle.BottomRight(); eKind = SdrHdlKind::LowerRight; break; } if (maGeo.m_nShearAngle) { ShearPoint(aPnt,rRectangle.TopLeft(),maGeo.mfTanShearAngle); } if (maGeo.m_nRotationAngle) { RotatePoint(aPnt,rRectangle.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle); } std::unique_ptr pH(new SdrHdl(aPnt,eKind)); pH->SetObj(const_cast(this)); pH->SetRotationAngle(maGeo.m_nRotationAngle); rHdlList.AddHdl(std::move(pH)); } } bool SdrRectObj::hasSpecialDrag() const { return true; } bool SdrRectObj::beginSpecialDrag(SdrDragStat& rDrag) const { const bool bRad(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind()); if(bRad) { rDrag.SetEndDragChangesAttributes(true); return true; } return SdrTextObj::beginSpecialDrag(rDrag); } bool SdrRectObj::applySpecialDrag(SdrDragStat& rDrag) { const bool bRad(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind()); if (bRad) { Point aPt(rDrag.GetNow()); if (maGeo.m_nRotationAngle) RotatePoint(aPt, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); sal_Int32 nRad(aPt.X() - getRectangle().Left()); if (nRad < 0) nRad = 0; if(nRad != GetEckenradius()) { NbcSetEckenradius(nRad); } return true; } else { return SdrTextObj::applySpecialDrag(rDrag); } } OUString SdrRectObj::getSpecialDragComment(const SdrDragStat& rDrag) const { const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj()); if(bCreateComment) { return OUString(); } else { const bool bRad(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind()); if(bRad) { Point aPt(rDrag.GetNow()); // -sin for reversal if (maGeo.m_nRotationAngle) RotatePoint(aPt, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); sal_Int32 nRad(aPt.X() - getRectangle().Left()); if(nRad < 0) nRad = 0; return ImpGetDescriptionStr(STR_DragRectEckRad) + " (" + GetMetrStr(nRad) + ")"; } else { return SdrTextObj::getSpecialDragComment(rDrag); } } } basegfx::B2DPolyPolygon SdrRectObj::TakeCreatePoly(const SdrDragStat& rDrag) const { tools::Rectangle aRect1; rDrag.TakeCreateRect(aRect1); aRect1.Normalize(); basegfx::B2DPolyPolygon aRetval; aRetval.append(ImpCalcXPoly(aRect1,GetEckenradius()).getB2DPolygon()); return aRetval; } PointerStyle SdrRectObj::GetCreatePointer() const { if (IsTextFrame()) return PointerStyle::DrawText; return PointerStyle::DrawRect; } void SdrRectObj::NbcMove(const Size& rSiz) { SdrTextObj::NbcMove(rSiz); SetXPolyDirty(); } void SdrRectObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) { SdrTextObj::NbcResize(rRef,xFact,yFact); SetXPolyDirty(); } void SdrRectObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs) { SdrTextObj::NbcRotate(rRef,nAngle,sn,cs); SetXPolyDirty(); } void SdrRectObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear) { SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear); SetXPolyDirty(); } void SdrRectObj::NbcMirror(const Point& rRef1, const Point& rRef2) { SdrTextObj::NbcMirror(rRef1,rRef2); SetXPolyDirty(); } SdrGluePoint SdrRectObj::GetVertexGluePoint(sal_uInt16 nPosNum) const { sal_Int32 nWdt = ImpGetLineWdt(); // #i25616# // #i25616# if(!LineIsOutsideGeometry()) { nWdt++; nWdt /= 2; } Point aPt; auto const& rRectangle = getRectangle(); switch (nPosNum) { case 0: aPt = rRectangle.TopCenter(); aPt.AdjustY( -nWdt ); break; case 1: aPt = rRectangle.RightCenter(); aPt.AdjustX(nWdt ); break; case 2: aPt = rRectangle.BottomCenter(); aPt.AdjustY(nWdt ); break; case 3: aPt = rRectangle.LeftCenter(); aPt.AdjustX( -nWdt ); break; } if (maGeo.m_nShearAngle) ShearPoint(aPt, rRectangle.TopLeft(), maGeo.mfTanShearAngle); if (maGeo.m_nRotationAngle) RotatePoint(aPt, rRectangle.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); aPt-=GetSnapRect().Center(); SdrGluePoint aGP(aPt); aGP.SetPercent(false); return aGP; } SdrGluePoint SdrRectObj::GetCornerGluePoint(sal_uInt16 nPosNum) const { sal_Int32 nWdt = ImpGetLineWdt(); // #i25616# // #i25616# if(!LineIsOutsideGeometry()) { nWdt++; nWdt /= 2; } Point aPt; auto const& rRectangle = getRectangle(); switch (nPosNum) { case 0: aPt = rRectangle.TopLeft(); aPt.AdjustX( -nWdt ); aPt.AdjustY( -nWdt ); break; case 1: aPt = rRectangle.TopRight(); aPt.AdjustX(nWdt ); aPt.AdjustY( -nWdt ); break; case 2: aPt = rRectangle.BottomRight(); aPt.AdjustX(nWdt ); aPt.AdjustY(nWdt ); break; case 3: aPt = rRectangle.BottomLeft(); aPt.AdjustX( -nWdt ); aPt.AdjustY(nWdt ); break; } if (maGeo.m_nShearAngle) ShearPoint(aPt, rRectangle.TopLeft(),maGeo.mfTanShearAngle); if (maGeo.m_nRotationAngle) RotatePoint(aPt, rRectangle.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle); aPt-=GetSnapRect().Center(); SdrGluePoint aGP(aPt); aGP.SetPercent(false); return aGP; } rtl::Reference SdrRectObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const { XPolygon aXP(ImpCalcXPoly(getRectangle(), GetEckenradius())); { // TODO: this is only for the moment, until we have the new TakeContour() aXP.Remove(0,1); aXP[aXP.GetPointCount()-1]=aXP[0]; } basegfx::B2DPolyPolygon aPolyPolygon(aXP.getB2DPolygon()); aPolyPolygon.removeDoublePoints(); rtl::Reference pRet; // small correction: Do not create something when no fill and no line. To // be sure to not damage something with non-text frames, do this only // when used with bAddText==false from other converters if((bAddText && !IsTextFrame()) || HasFill() || HasLine()) { pRet = ImpConvertMakeObj(aPolyPolygon, true, bBezier); } if(bAddText) { pRet = ImpConvertAddText(std::move(pRet), bBezier); } return pRet; } void SdrRectObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) { SdrTextObj::Notify(rBC,rHint); SetXPolyDirty(); // because of the corner radius } void SdrRectObj::RestoreGeoData(const SdrObjGeoData& rGeo) { SdrTextObj::RestoreGeoData(rGeo); SetXPolyDirty(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */