1
0
Fork 0
libreoffice/svx/source/svdraw/svdorect.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

573 lines
17 KiB
C++

/* -*- 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 <svx/svdorect.hxx>
#include <svx/xpoly.hxx>
#include <svx/svdtrans.hxx>
#include <svx/svddrag.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdview.hxx>
#include <svx/svdopath.hxx>
#include <svx/dialmgr.hxx>
#include <svx/strings.hrc>
#include <sdr/properties/rectangleproperties.hxx>
#include <sdr/contact/viewcontactofsdrrectobj.hxx>
#include <tools/debug.hxx>
#include <vcl/ptrstyle.hxx>
#include <osl/diagnose.h>
using namespace com::sun::star;
// BaseProperties section
std::unique_ptr<sdr::properties::BaseProperties> SdrRectObj::CreateObjectSpecificProperties()
{
return std::make_unique<sdr::properties::RectangleProperties>(*this);
}
// DrawContact section
std::unique_ptr<sdr::contact::ViewContact> SdrRectObj::CreateObjectSpecificViewContact()
{
return std::make_unique<sdr::contact::ViewContactOfSdrRectObj>(*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; i++) {
aNewPoly[i]=aXPoly[j];
aNewPoly.SetFlags(i,aXPoly.GetFlags(j));
j++;
if (j>=nPointCnt) j=1;
}
aNewPoly[0]=rRect1.BottomCenter();
aNewPoly[nPointCnt]=aNewPoly[0];
aXPoly=std::move(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<SdrRectObj*>(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<tools::Long>((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<SdrObject> 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, bool bAdaptTextMinSize)
{
SdrTextObj::NbcSetLogicRect(rRect, bAdaptTextMinSize);
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<SdrHdl> pH(new ImpTextframeHdl(getRectangle()));
pH->SetObj(const_cast<SdrRectObj*>(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<SdrHdl> pH(new SdrHdl(aPnt,eKind));
pH->SetObj(const_cast<SdrRectObj*>(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<SdrObject> 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<SdrObject> 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: */