Files
odfstudio/svx/source/svdraw/svdomeas.cxx
T
Armin Le Grand (collabora) 5519a3d407 Correct StyleSheet handling when cloning to different Model II
Until now the 'local' StyleSheet got set, but that may
not be the same as the original. It was/gets identified just
by searching by name and type, not comparing content.

What to do? We could...

(a) the Style-Friendly way:
 - check if StyleSheet is identical
  - if not, create a new in-between StyleSheet hosting missing Items
  - if yes, use, done.

(b) the pragmatic/practical way:
 - Just hard-attribute the Items that are detected as different
   similar to what BurnInStyleSheetAttributes does/did, but only
   for a view differing Items

(b) is the most simple for now, but will lead to more hard
attributes. The other possibilities will be better in the
long term. For now, I will use (b).

Note that there are more cases in that VERY complex and grown
stuff emerging from SdDrawDocument::PasteBookmarkAsPage. More
things will need to be fixed, see this as a 1st step.

Needed to adapt postProcessAfterCloneToDifferentModel due to
UT testTdf96206 failing, see comment there for details.

That 1st implementation showed weaknesses with those Items
critical and only on MasterPages, so decided to give up on
that and implement ContentCompare to SdPage::Equals. That
was easier than thought: No parameter needed, only used by
InsertBookmarkAsPage_FindDuplicateLayouts::operator(). Thus,
SdPage::Equals needs no change, just SdrObject::Equals.

Due to still problems with Items corrected in Objects on
MasterPages I will for now exclude this.

Could now isolate remaining problems, these are defaults
and/or PresObj things on MasterPages. Important is that I can
completely correct all Items for all Objects in
postProcessAfterCloneToDifferentModel. I can react to these
false-positives in the ContentCompare in SdrObject::Equals.

Signed-off-by: Armin Le Grand (collabora) <Armin.LeGrand@collabora.com>
Change-Id: I10e17c8d6a893dbe92ab716182f07bafce1f3284
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/6595
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Armin Le Grand <armin.legrand@collabora.com>
(cherry picked from commit 92ffaeac04c5c8586006b026f29ce12fba3674f1)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211729
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
2026-10-05 08:52:54 +02:00

1427 lines
48 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/dialmgr.hxx>
#include <svx/strings.hrc>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <editeng/editdata.hxx>
#include <editeng/editobj.hxx>
#include <editeng/eeitem.hxx>
#include <editeng/flditem.hxx>
#include <editeng/measfld.hxx>
#include <editeng/outlobj.hxx>
#include <math.h>
#include <svl/style.hxx>
#include <sdr/contact/viewcontactofsdrmeasureobj.hxx>
#include <sdr/properties/measureproperties.hxx>
#include <svx/svddrag.hxx>
#include <svx/svdhdl.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdogrp.hxx>
#include <svx/svdomeas.hxx>
#include <svx/svdopath.hxx>
#include <svx/svdoutl.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdtrans.hxx>
#include <svx/svdview.hxx>
#include <svx/sxmbritm.hxx>
#include <svx/sxmlhitm.hxx>
#include <sxmsitm.hxx>
#include <sxmtaitm.hxx>
#include <svx/sxmtfitm.hxx>
#include <svx/sxmtpitm.hxx>
#include <svx/sxmtritm.hxx>
#include <svx/sxmuitm.hxx>
#include <svx/xlnedcit.hxx>
#include <svx/xlnedit.hxx>
#include <svx/xlnedwit.hxx>
#include <svx/xlnstcit.hxx>
#include <svx/xlnstit.hxx>
#include <svx/xlnstwit.hxx>
#include <svx/xlnwtit.hxx>
#include <svx/xpoly.hxx>
#include <unotools/syslocale.hxx>
#include <unotools/localedatawrapper.hxx>
#include <vcl/ptrstyle.hxx>
SdrMeasureObjGeoData::SdrMeasureObjGeoData() {}
SdrMeasureObjGeoData::~SdrMeasureObjGeoData() {}
OUString SdrMeasureObj::TakeRepresentation(SdrMeasureFieldKind eMeasureFieldKind) const
{
OUString aStr;
double fMeasureScale = 1.0;
bool bTextRota90(false);
bool bShowUnit(false);
FieldUnit eMeasureUnit(FieldUnit::NONE);
FieldUnit eModUIUnit(FieldUnit::NONE);
const SfxItemSet& rSet = GetMergedItemSet();
bTextRota90 = rSet.Get(SDRATTR_MEASURETEXTROTA90).GetValue();
eMeasureUnit = rSet.Get(SDRATTR_MEASUREUNIT).GetValue();
fMeasureScale = rSet.Get(SDRATTR_MEASURESCALE).GetValue();
bShowUnit = rSet.Get(SDRATTR_MEASURESHOWUNIT).GetValue();
sal_Int16 nNumDigits = rSet.Get(SDRATTR_MEASUREDECIMALPLACES).GetValue();
switch(eMeasureFieldKind)
{
case SdrMeasureFieldKind::Value:
{
eModUIUnit = getSdrModelFromSdrObject().GetUIUnit();
if(eMeasureUnit == FieldUnit::NONE)
eMeasureUnit = eModUIUnit;
sal_Int32 nLen(GetLen(m_aPt2 - m_aPt1));
double fFact = 1.0;
if(eMeasureUnit != eModUIUnit)
{
// for the unit conversion
fFact *= GetMapFactor(eModUIUnit, eMeasureUnit).X();
}
if(fMeasureScale != 1.0)
{
fFact *= fMeasureScale;
}
if(fFact != 1.0)
{
nLen = nLen * fFact;
}
aStr = getSdrModelFromSdrObject().GetMetricString(nLen, true, nNumDigits);
// tdf#138414 - respect decimal precision for dimension lines
const auto bAutoNumDigits = rSet.Get(SDRATTR_MEASUREAUTODECIMALPLACES).GetValue();
if (bAutoNumDigits)
{
SvtSysLocale aSysLocale;
const LocaleDataWrapper& rLocaleDataWrapper = aSysLocale.GetLocaleData();
sal_Unicode cDec(rLocaleDataWrapper.getNumDecimalSep()[0]);
sal_Unicode cDecAlt(rLocaleDataWrapper.getNumDecimalSepAlt().toChar());
if (aStr.indexOf(cDec) != -1 || (cDecAlt && aStr.indexOf(cDecAlt) != -1))
{
sal_Int32 nLen2(aStr.getLength() - 1);
while (aStr[nLen2] == '0')
{
aStr = aStr.copy(0, nLen2);
nLen2--;
}
if (aStr[nLen2] == cDec || (cDecAlt && aStr[nLen2] == cDecAlt))
{
aStr = aStr.copy(0, nLen2);
nLen2--;
}
if (aStr.isEmpty())
aStr += "0";
}
}
break;
}
case SdrMeasureFieldKind::Unit:
{
if(bShowUnit)
{
eModUIUnit = getSdrModelFromSdrObject().GetUIUnit();
if(eMeasureUnit == FieldUnit::NONE)
eMeasureUnit = eModUIUnit;
aStr = SdrModel::GetUnitString(eMeasureUnit);
}
break;
}
case SdrMeasureFieldKind::Rotate90Blanks:
{
if(bTextRota90)
{
aStr = " ";
}
break;
}
}
return aStr;
}
// BaseProperties section
std::unique_ptr<sdr::properties::BaseProperties> SdrMeasureObj::CreateObjectSpecificProperties()
{
return std::make_unique<sdr::properties::MeasureProperties>(*this);
}
// DrawContact section
std::unique_ptr<sdr::contact::ViewContact> SdrMeasureObj::CreateObjectSpecificViewContact()
{
return std::make_unique<sdr::contact::ViewContactOfSdrMeasureObj>(*this);
}
SdrMeasureObj::SdrMeasureObj(SdrModel& rSdrModel)
: SdrTextObj(rSdrModel),
m_bTextDirty(false)
{
// #i25616#
mbSupportTextIndentingOnLineWidthChange = false;
}
SdrMeasureObj::SdrMeasureObj(SdrModel& rSdrModel, SdrMeasureObj const & rSource)
: SdrTextObj(rSdrModel, rSource),
m_bTextDirty(false)
{
// #i25616#
mbSupportTextIndentingOnLineWidthChange = false;
m_aPt1 = rSource.m_aPt1;
m_aPt2 = rSource.m_aPt2;
m_bTextDirty = rSource.m_bTextDirty;
}
SdrMeasureObj::SdrMeasureObj(
SdrModel& rSdrModel,
const Point& rPt1,
const Point& rPt2)
: SdrTextObj(rSdrModel),
m_aPt1(rPt1),
m_aPt2(rPt2),
m_bTextDirty(false)
{
// #i25616#
mbSupportTextIndentingOnLineWidthChange = false;
}
SdrMeasureObj::~SdrMeasureObj()
{
}
void SdrMeasureObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
{
rInfo.bMoveAllowed =true;
rInfo.bResizeFreeAllowed=true;
rInfo.bResizePropAllowed=true;
rInfo.bRotateFreeAllowed=true;
rInfo.bRotate90Allowed =true;
rInfo.bMirrorFreeAllowed=true;
rInfo.bMirror45Allowed =true;
rInfo.bMirror90Allowed =true;
rInfo.bTransparenceAllowed = false;
rInfo.bShearAllowed =true;
rInfo.bEdgeRadiusAllowed=false;
rInfo.bNoOrthoDesired =true;
rInfo.bNoContortion =false;
rInfo.bCanConvToPath =false;
rInfo.bCanConvToPoly =true;
rInfo.bCanConvToPathLineToArea=false;
rInfo.bCanConvToPolyLineToArea=false;
rInfo.bCanConvToContour = LineGeometryUsageIsNecessary();
}
SdrObjKind SdrMeasureObj::GetObjIdentifier() const
{
return SdrObjKind::Measure;
}
struct ImpMeasureRec : public SdrDragStatUserData
{
Point aPt1;
Point aPt2;
css::drawing::MeasureTextHorzPos eWantTextHPos;
css::drawing::MeasureTextVertPos eWantTextVPos;
tools::Long nLineDist;
tools::Long nHelplineOverhang;
tools::Long nHelplineDist;
tools::Long nHelpline1Len;
tools::Long nHelpline2Len;
bool bBelowRefEdge;
bool bTextRota90;
bool bTextUpsideDown;
bool bTextAutoAngle;
Degree100 nTextAutoAngleView;
};
namespace {
struct ImpLineRec
{
Point aP1;
Point aP2;
};
}
struct ImpMeasurePoly
{
ImpLineRec aMainline1; // those with the 1st arrowhead
ImpLineRec aMainline2; // those with the 2nd arrowhead
ImpLineRec aMainline3; // those in between
ImpLineRec aHelpline1;
ImpLineRec aHelpline2;
Size aTextSize;
tools::Long nLineLen;
Degree100 nLineAngle;
Degree100 nTextAngle;
Degree100 nHlpAngle;
double nLineSin;
double nLineCos;
sal_uInt16 nMainlineCnt;
css::drawing::MeasureTextHorzPos eUsedTextHPos;
css::drawing::MeasureTextVertPos eUsedTextVPos;
tools::Long nLineWdt2; // half the line width
tools::Long nArrow1Len; // length of 1st arrowhead; for Center, use only half
tools::Long nArrow2Len; // length of 2nd arrowhead; for Center, use only half
tools::Long nArrow1Wdt; // width of 1st arrow
tools::Long nArrow2Wdt; // width of 2nd arrow
tools::Long nShortLineLen; // line length, if PfeileAussen (arrowheads on the outside)
bool bAutoUpsideDown; // UpsideDown via automation
bool bBreakedLine;
};
void SdrMeasureObj::ImpTakeAttr(ImpMeasureRec& rRec) const
{
rRec.aPt1 = m_aPt1;
rRec.aPt2 = m_aPt2;
const SfxItemSet& rSet = GetObjectItemSet();
rRec.eWantTextHPos =rSet.Get(SDRATTR_MEASURETEXTHPOS ).GetValue();
rRec.eWantTextVPos =rSet.Get(SDRATTR_MEASURETEXTVPOS ).GetValue();
rRec.nLineDist =rSet.Get(SDRATTR_MEASURELINEDIST ).GetValue();
rRec.nHelplineOverhang =rSet.Get(SDRATTR_MEASUREHELPLINEOVERHANG).GetValue();
rRec.nHelplineDist =rSet.Get(SDRATTR_MEASUREHELPLINEDIST ).GetValue();
rRec.nHelpline1Len =rSet.Get(SDRATTR_MEASUREHELPLINE1LEN ).GetValue();
rRec.nHelpline2Len =rSet.Get(SDRATTR_MEASUREHELPLINE2LEN ).GetValue();
rRec.bBelowRefEdge =rSet.Get(SDRATTR_MEASUREBELOWREFEDGE ).GetValue();
rRec.bTextRota90 =rSet.Get(SDRATTR_MEASURETEXTROTA90 ).GetValue();
rRec.bTextUpsideDown =static_cast<const SdrMeasureTextUpsideDownItem& >(rSet.Get(SDRATTR_MEASURETEXTUPSIDEDOWN )).GetValue();
rRec.bTextAutoAngle =rSet.Get(SDRATTR_MEASURETEXTAUTOANGLE ).GetValue();
rRec.nTextAutoAngleView=static_cast<const SdrMeasureTextAutoAngleViewItem&>(rSet.Get(SDRATTR_MEASURETEXTAUTOANGLEVIEW)).GetValue();
}
static tools::Long impGetLineStartEndDistance(const basegfx::B2DPolyPolygon& rPolyPolygon, tools::Long nNewWidth, bool bCenter)
{
const basegfx::B2DRange aPolygonRange(rPolyPolygon.getB2DRange());
const double fOldWidth(std::max(aPolygonRange.getWidth(), 1.0));
const double fScale(static_cast<double>(nNewWidth) / fOldWidth);
tools::Long nHeight(basegfx::fround<tools::Long>(aPolygonRange.getHeight() * fScale));
if(bCenter)
{
nHeight /= 2;
}
return nHeight;
}
void SdrMeasureObj::ImpCalcGeometrics(const ImpMeasureRec& rRec, ImpMeasurePoly& rPol) const
{
Point aP1(rRec.aPt1);
Point aP2(rRec.aPt2);
Point aDelt(aP2); aDelt-=aP1;
rPol.aTextSize=GetTextSize();
rPol.nLineLen=GetLen(aDelt);
rPol.nLineWdt2=0;
tools::Long nArrow1Len=0; bool bArrow1Center=false;
tools::Long nArrow2Len=0; bool bArrow2Center=false;
tools::Long nArrow1Wdt=0;
tools::Long nArrow2Wdt=0;
rPol.nArrow1Wdt=0;
rPol.nArrow2Wdt=0;
tools::Long nArrowNeed=0;
tools::Long nShortLen=0;
bool bPfeileAussen = false;
const SfxItemSet& rSet = GetObjectItemSet();
sal_Int32 nLineWdt = rSet.Get(XATTR_LINEWIDTH).GetValue(); // line width
rPol.nLineWdt2 = (nLineWdt + 1) / 2;
nArrow1Wdt = rSet.Get(XATTR_LINESTARTWIDTH).GetValue();
if(nArrow1Wdt < 0)
nArrow1Wdt = -nLineWdt * nArrow1Wdt / 100; // <0 = relative
nArrow2Wdt = rSet.Get(XATTR_LINEENDWIDTH).GetValue();
if(nArrow2Wdt < 0)
nArrow2Wdt = -nLineWdt * nArrow2Wdt / 100; // <0 = relative
basegfx::B2DPolyPolygon aPol1(rSet.Get(XATTR_LINESTART).GetLineStartValue());
basegfx::B2DPolyPolygon aPol2(rSet.Get(XATTR_LINEEND).GetLineEndValue());
bArrow1Center = rSet.Get(XATTR_LINESTARTCENTER).GetValue();
bArrow2Center = rSet.Get(XATTR_LINEENDCENTER).GetValue();
nArrow1Len = impGetLineStartEndDistance(aPol1, nArrow1Wdt, bArrow1Center) - 1;
nArrow2Len = impGetLineStartEndDistance(aPol2, nArrow2Wdt, bArrow2Center) - 1;
// nArrowLen is already halved at bCenter.
// In the case of 2 arrowheads each 4mm long, we can't go below 10mm.
nArrowNeed=nArrow1Len+nArrow2Len+(nArrow1Wdt+nArrow2Wdt)/2;
if (rPol.nLineLen<nArrowNeed) bPfeileAussen = true;
nShortLen=(nArrow1Len+nArrow1Wdt + nArrow2Len+nArrow2Wdt) /2;
rPol.eUsedTextHPos=rRec.eWantTextHPos;
rPol.eUsedTextVPos=rRec.eWantTextVPos;
if (rPol.eUsedTextVPos == css::drawing::MeasureTextVertPos_AUTO)
rPol.eUsedTextVPos = css::drawing::MeasureTextVertPos_EAST;
bool bBrkLine=false;
if (rPol.eUsedTextVPos == css::drawing::MeasureTextVertPos_CENTERED)
{
OutlinerParaObject* pOutlinerParaObject = SdrTextObj::GetOutlinerParaObject();
if (pOutlinerParaObject!=nullptr && pOutlinerParaObject->GetTextObject().GetParagraphCount()==1)
{
bBrkLine=true; // dashed line if there's only on paragraph.
}
}
rPol.bBreakedLine=bBrkLine;
if (rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_AUTO) { // if text is too wide, push it outside
bool bOutside = false;
tools::Long nNeedSiz=!rRec.bTextRota90 ? rPol.aTextSize.Width() : rPol.aTextSize.Height();
if (nNeedSiz>rPol.nLineLen) bOutside = true; // text doesn't fit in between
if (bBrkLine) {
if (nNeedSiz+nArrowNeed>rPol.nLineLen) bPfeileAussen = true; // text fits in between, if arrowheads are on the outside
} else {
tools::Long nSmallNeed=nArrow1Len+nArrow2Len+(nArrow1Wdt+nArrow2Wdt)/2/4;
if (nNeedSiz+nSmallNeed>rPol.nLineLen) bPfeileAussen = true; // text fits in between, if arrowheads are on the outside
}
rPol.eUsedTextHPos=bOutside ? css::drawing::MeasureTextHorzPos_LEFTOUTSIDE : css::drawing::MeasureTextHorzPos_INSIDE;
}
if (rPol.eUsedTextHPos != css::drawing::MeasureTextHorzPos_INSIDE) bPfeileAussen = true;
rPol.nArrow1Wdt=nArrow1Wdt;
rPol.nArrow2Wdt=nArrow2Wdt;
rPol.nShortLineLen=nShortLen;
rPol.nArrow1Len=nArrow1Len;
rPol.nArrow2Len=nArrow2Len;
rPol.nLineAngle=GetAngle(aDelt);
double a = toRadians(rPol.nLineAngle);
double nLineSin=sin(a);
double nLineCos=cos(a);
rPol.nLineSin=nLineSin;
rPol.nLineCos=nLineCos;
rPol.nTextAngle=rPol.nLineAngle;
if (rRec.bTextRota90) rPol.nTextAngle+=9000_deg100;
rPol.bAutoUpsideDown=false;
if (rRec.bTextAutoAngle) {
Degree100 nTmpAngle=NormAngle36000(rPol.nTextAngle-rRec.nTextAutoAngleView);
if (nTmpAngle>=18000_deg100) {
rPol.nTextAngle+=18000_deg100;
rPol.bAutoUpsideDown=true;
}
}
if (rRec.bTextUpsideDown) rPol.nTextAngle+=18000_deg100;
rPol.nTextAngle=NormAngle36000(rPol.nTextAngle);
rPol.nHlpAngle=rPol.nLineAngle+9000_deg100;
if (rRec.bBelowRefEdge) rPol.nHlpAngle+=18000_deg100;
rPol.nHlpAngle=NormAngle36000(rPol.nHlpAngle);
double nHlpSin=nLineCos;
double nHlpCos=-nLineSin;
if (rRec.bBelowRefEdge) {
nHlpSin=-nHlpSin;
nHlpCos=-nHlpCos;
}
tools::Long nLineDist=rRec.nLineDist;
tools::Long nOverhang=rRec.nHelplineOverhang;
tools::Long nHelplineDist=rRec.nHelplineDist;
tools::Long dx = basegfx::fround<tools::Long>(nLineDist * nHlpCos);
tools::Long dy = basegfx::fround<tools::Long>(nLineDist * -nHlpSin);
tools::Long dxh1a = basegfx::fround<tools::Long>((nHelplineDist - rRec.nHelpline1Len) * nHlpCos);
tools::Long dyh1a = basegfx::fround<tools::Long>((nHelplineDist - rRec.nHelpline1Len) * -nHlpSin);
tools::Long dxh1b = basegfx::fround<tools::Long>((nHelplineDist - rRec.nHelpline2Len) * nHlpCos);
tools::Long dyh1b = basegfx::fround<tools::Long>((nHelplineDist - rRec.nHelpline2Len) * -nHlpSin);
tools::Long dxh2 = basegfx::fround<tools::Long>((nLineDist + nOverhang) * nHlpCos);
tools::Long dyh2 = basegfx::fround<tools::Long>((nLineDist + nOverhang) * -nHlpSin);
// extension line 1
rPol.aHelpline1.aP1=Point(aP1.X()+dxh1a,aP1.Y()+dyh1a);
rPol.aHelpline1.aP2=Point(aP1.X()+dxh2,aP1.Y()+dyh2);
// extension line 2
rPol.aHelpline2.aP1=Point(aP2.X()+dxh1b,aP2.Y()+dyh1b);
rPol.aHelpline2.aP2=Point(aP2.X()+dxh2,aP2.Y()+dyh2);
// dimension line
Point aMainlinePt1(aP1.X()+dx,aP1.Y()+dy);
Point aMainlinePt2(aP2.X()+dx,aP2.Y()+dy);
if (!bPfeileAussen) {
rPol.aMainline1.aP1=aMainlinePt1;
rPol.aMainline1.aP2=aMainlinePt2;
rPol.aMainline2=rPol.aMainline1;
rPol.aMainline3=rPol.aMainline1;
rPol.nMainlineCnt=1;
if (bBrkLine) {
tools::Long nNeedSiz=!rRec.bTextRota90 ? rPol.aTextSize.Width() : rPol.aTextSize.Height();
tools::Long nHalfLen=(rPol.nLineLen-nNeedSiz-nArrow1Wdt/4-nArrow2Wdt/4) /2;
rPol.nMainlineCnt=2;
rPol.aMainline1.aP2=aMainlinePt1;
rPol.aMainline1.aP2.AdjustX(nHalfLen );
RotatePoint(rPol.aMainline1.aP2,rPol.aMainline1.aP1,nLineSin,nLineCos);
rPol.aMainline2.aP1=aMainlinePt2;
rPol.aMainline2.aP1.AdjustX( -nHalfLen );
RotatePoint(rPol.aMainline2.aP1,rPol.aMainline2.aP2,nLineSin,nLineCos);
}
} else {
tools::Long nLen1=nShortLen; // arrowhead's width as line length outside of the arrowhead
tools::Long nLen2=nShortLen;
tools::Long nTextWdt=rRec.bTextRota90 ? rPol.aTextSize.Height() : rPol.aTextSize.Width();
if (!bBrkLine) {
if (rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_LEFTOUTSIDE) nLen1=nArrow1Len+nTextWdt;
if (rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE) nLen2=nArrow2Len+nTextWdt;
}
rPol.aMainline1.aP1=aMainlinePt1;
rPol.aMainline1.aP2=aMainlinePt1; rPol.aMainline1.aP2.AdjustX( -nLen1 ); RotatePoint(rPol.aMainline1.aP2,aMainlinePt1,nLineSin,nLineCos);
rPol.aMainline2.aP1=aMainlinePt2; rPol.aMainline2.aP1.AdjustX(nLen2 ); RotatePoint(rPol.aMainline2.aP1,aMainlinePt2,nLineSin,nLineCos);
rPol.aMainline2.aP2=aMainlinePt2;
rPol.aMainline3.aP1=aMainlinePt1;
rPol.aMainline3.aP2=aMainlinePt2;
rPol.nMainlineCnt=3;
if (bBrkLine && rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_INSIDE) rPol.nMainlineCnt=2;
}
}
basegfx::B2DPolyPolygon SdrMeasureObj::ImpCalcXPoly(const ImpMeasurePoly& rPol)
{
basegfx::B2DPolyPolygon aRetval;
basegfx::B2DPolygon aPartPolyA;
aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline1.aP1.X(), rPol.aMainline1.aP1.Y()));
aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline1.aP2.X(), rPol.aMainline1.aP2.Y()));
aRetval.append(aPartPolyA);
if(rPol.nMainlineCnt > 1)
{
aPartPolyA.clear();
aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline2.aP1.X(), rPol.aMainline2.aP1.Y()));
aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline2.aP2.X(), rPol.aMainline2.aP2.Y()));
aRetval.append(aPartPolyA);
}
if(rPol.nMainlineCnt > 2)
{
aPartPolyA.clear();
aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline3.aP1.X(), rPol.aMainline3.aP1.Y()));
aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline3.aP2.X(), rPol.aMainline3.aP2.Y()));
aRetval.append(aPartPolyA);
}
aPartPolyA.clear();
aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline1.aP1.X(), rPol.aHelpline1.aP1.Y()));
aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline1.aP2.X(), rPol.aHelpline1.aP2.Y()));
aRetval.append(aPartPolyA);
aPartPolyA.clear();
aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline2.aP1.X(), rPol.aHelpline2.aP1.Y()));
aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline2.aP2.X(), rPol.aHelpline2.aP2.Y()));
aRetval.append(aPartPolyA);
return aRetval;
}
bool SdrMeasureObj::CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_uInt16 nPos,
bool bEdit,
std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor, std::optional<FontLineStyle>& rpFldLineStyle, OUString& rRet) const
{
const SvxFieldData* pField=rField.GetField();
const SdrMeasureField* pMeasureField=dynamic_cast<const SdrMeasureField*>( pField );
if (pMeasureField!=nullptr) {
rRet = TakeRepresentation(pMeasureField->GetMeasureFieldKind());
if (rpFldColor && !bEdit)
{
rpFldColor.reset();
}
return true;
} else {
return SdrTextObj::CalcFieldValue(rField,nPara,nPos,bEdit,rpTxtColor,rpFldColor,rpFldLineStyle,rRet);
}
}
void SdrMeasureObj::UndirtyText() const
{
if (!m_bTextDirty)
return;
SdrOutliner& rOutliner=ImpGetDrawOutliner();
OutlinerParaObject* pOutlinerParaObject = SdrTextObj::GetOutlinerParaObject();
if(pOutlinerParaObject==nullptr)
{
rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Rotate90Blanks), EE_FEATURE_FIELD), ESelection(0,0));
rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Value), EE_FEATURE_FIELD),ESelection(0,1));
rOutliner.QuickInsertText(u" "_ustr, ESelection(0,2));
rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Unit), EE_FEATURE_FIELD),ESelection(0,3));
rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Rotate90Blanks), EE_FEATURE_FIELD),ESelection(0,4));
if(GetStyleSheet())
rOutliner.SetStyleSheet(0, GetStyleSheet());
rOutliner.SetParaAttribs(0, GetObjectItemSet());
// cast to nonconst
const_cast<SdrMeasureObj*>(this)->NbcSetOutlinerParaObject( rOutliner.CreateParaObject() );
}
else
{
rOutliner.SetText(*pOutlinerParaObject);
}
rOutliner.SetUpdateLayout(true);
rOutliner.UpdateFields();
Size aSiz(rOutliner.CalcTextSize());
rOutliner.Clear();
// cast to nonconst three times
const_cast<SdrMeasureObj*>(this)->maTextSize = aSiz;
const_cast<SdrMeasureObj*>(this)->mbTextSizeDirty = false;
const_cast<SdrMeasureObj*>(this)->m_bTextDirty = false;
}
void SdrMeasureObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
{
if (m_bTextDirty) UndirtyText();
ImpMeasureRec aRec;
ImpMeasurePoly aMPol;
ImpTakeAttr(aRec);
ImpCalcGeometrics(aRec,aMPol);
// determine TextSize including text frame margins
Size aTextSize2(aMPol.aTextSize);
if (aTextSize2.Width()<1) aTextSize2.setWidth(1 );
if (aTextSize2.Height()<1) aTextSize2.setHeight(1 );
aTextSize2.AdjustWidth(GetTextLeftDistance()+GetTextRightDistance() );
aTextSize2.AdjustHeight(GetTextUpperDistance()+GetTextLowerDistance() );
Point aPt1b(aMPol.aMainline1.aP1);
tools::Long nLen=aMPol.nLineLen;
tools::Long nLWdt=aMPol.nLineWdt2;
tools::Long nArr1Len=aMPol.nArrow1Len;
tools::Long nArr2Len=aMPol.nArrow2Len;
if (aMPol.bBreakedLine) {
// In the case of a dashed line and Outside, the text should be
// placed next to the line at the arrowhead instead of directly
// at the arrowhead.
nArr1Len=aMPol.nShortLineLen+aMPol.nArrow1Wdt/4;
nArr2Len=aMPol.nShortLineLen+aMPol.nArrow2Wdt/4;
}
Point aTextPos;
bool bRota90=aRec.bTextRota90;
bool bUpsideDown=aRec.bTextUpsideDown!=aMPol.bAutoUpsideDown;
bool bBelowRefEdge=aRec.bBelowRefEdge;
css::drawing::MeasureTextHorzPos eMH=aMPol.eUsedTextHPos;
css::drawing::MeasureTextVertPos eMV=aMPol.eUsedTextVPos;
if (!bRota90) {
switch (eMH) {
case css::drawing::MeasureTextHorzPos_LEFTOUTSIDE: aTextPos.setX(aPt1b.X()-aTextSize2.Width()-nArr1Len-nLWdt ); break;
case css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE: aTextPos.setX(aPt1b.X()+nLen+nArr2Len+nLWdt ); break;
default: aTextPos.setX(aPt1b.X() ); aTextSize2.setWidth(nLen );
}
switch (eMV) {
case css::drawing::MeasureTextVertPos_CENTERED:
aTextPos.setY(aPt1b.Y()-aTextSize2.Height()/2 ); break;
case css::drawing::MeasureTextVertPos_WEST: {
if (!bUpsideDown) aTextPos.setY(aPt1b.Y()+nLWdt );
else aTextPos.setY(aPt1b.Y()-aTextSize2.Height()-nLWdt );
} break;
default: {
if (!bUpsideDown) aTextPos.setY(aPt1b.Y()-aTextSize2.Height()-nLWdt );
else aTextPos.setY(aPt1b.Y()+nLWdt );
}
}
if (bUpsideDown) {
aTextPos.AdjustX(aTextSize2.Width() );
aTextPos.AdjustY(aTextSize2.Height() );
}
} else { // also if bTextRota90==TRUE
switch (eMH) {
case css::drawing::MeasureTextHorzPos_LEFTOUTSIDE: aTextPos.setX(aPt1b.X()-aTextSize2.Height()-nArr1Len ); break;
case css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE: aTextPos.setX(aPt1b.X()+nLen+nArr2Len ); break;
default: aTextPos.setX(aPt1b.X() ); aTextSize2.setHeight(nLen );
}
switch (eMV) {
case css::drawing::MeasureTextVertPos_CENTERED:
aTextPos.setY(aPt1b.Y()+aTextSize2.Width()/2 ); break;
case css::drawing::MeasureTextVertPos_WEST: {
if (!bBelowRefEdge) aTextPos.setY(aPt1b.Y()+aTextSize2.Width()+nLWdt );
else aTextPos.setY(aPt1b.Y()-nLWdt );
} break;
default: {
if (!bBelowRefEdge) aTextPos.setY(aPt1b.Y()-nLWdt );
else aTextPos.setY(aPt1b.Y()+aTextSize2.Width()+nLWdt );
}
}
if (bUpsideDown) {
aTextPos.AdjustX(aTextSize2.Height() );
aTextPos.AdjustY( -(aTextSize2.Width()) );
}
}
if (aMPol.nTextAngle != maGeo.m_nRotationAngle) {
const_cast<SdrMeasureObj*>(this)->maGeo.m_nRotationAngle=aMPol.nTextAngle;
const_cast<SdrMeasureObj*>(this)->maGeo.RecalcSinCos();
}
RotatePoint(aTextPos,aPt1b,aMPol.nLineSin,aMPol.nLineCos);
aTextSize2.AdjustWidth( 1 ); aTextSize2.AdjustHeight( 1 ); // because of the Rect-Ctor's odd behavior
rRect=tools::Rectangle(aTextPos,aTextSize2);
rRect.Normalize();
const_cast<SdrMeasureObj*>(this)->setRectangle(rRect);
if (aMPol.nTextAngle != maGeo.m_nRotationAngle) {
const_cast<SdrMeasureObj*>(this)->maGeo.m_nRotationAngle=aMPol.nTextAngle;
const_cast<SdrMeasureObj*>(this)->maGeo.RecalcSinCos();
}
}
rtl::Reference<SdrObject> SdrMeasureObj::implCloneSdrObject(SdrModel& rTargetModel) const
{
return new SdrMeasureObj(rTargetModel, *this);
}
OUString SdrMeasureObj::TakeObjNameSingul() const
{
OUString sName(SvxResId(STR_ObjNameSingulMEASURE));
OUString aName( GetName() );
if (!aName.isEmpty())
sName += " '" + aName + "'";
return sName;
}
OUString SdrMeasureObj::TakeObjNamePlural() const
{
return SvxResId(STR_ObjNamePluralMEASURE);
}
basegfx::B2DPolyPolygon SdrMeasureObj::TakeXorPoly() const
{
ImpMeasureRec aRec;
ImpMeasurePoly aMPol;
ImpTakeAttr(aRec);
ImpCalcGeometrics(aRec,aMPol);
return ImpCalcXPoly(aMPol);
}
sal_uInt32 SdrMeasureObj::GetHdlCount() const
{
return 6;
}
void SdrMeasureObj::AddToHdlList(SdrHdlList& rHdlList) const
{
ImpMeasureRec aRec;
ImpMeasurePoly aMPol;
ImpTakeAttr(aRec);
aRec.nHelplineDist=0;
ImpCalcGeometrics(aRec,aMPol);
for (sal_uInt32 nHdlNum=0; nHdlNum<6; ++nHdlNum)
{
Point aPt;
switch (nHdlNum) {
case 0: aPt=aMPol.aHelpline1.aP1; break;
case 1: aPt=aMPol.aHelpline2.aP1; break;
case 2: aPt=m_aPt1; break;
case 3: aPt=m_aPt2; break;
case 4: aPt=aMPol.aHelpline1.aP2; break;
case 5: aPt=aMPol.aHelpline2.aP2; break;
} // switch
std::unique_ptr<SdrHdl> pHdl(new ImpMeasureHdl(aPt,SdrHdlKind::User));
pHdl->SetObjHdlNum(nHdlNum);
pHdl->SetRotationAngle(aMPol.nLineAngle);
rHdlList.AddHdl(std::move(pHdl));
}
}
bool SdrMeasureObj::hasSpecialDrag() const
{
return true;
}
bool SdrMeasureObj::beginSpecialDrag(SdrDragStat& rDrag) const
{
const SdrHdl* pHdl = rDrag.GetHdl();
if(pHdl)
{
const sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
if(nHdlNum != 2 && nHdlNum != 3)
{
rDrag.SetEndDragChangesAttributes(true);
}
return true;
}
return false;
}
bool SdrMeasureObj::applySpecialDrag(SdrDragStat& rDrag)
{
ImpMeasureRec aMeasureRec;
const SdrHdl* pHdl = rDrag.GetHdl();
const sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
ImpTakeAttr(aMeasureRec);
ImpEvalDrag(aMeasureRec, rDrag);
switch (nHdlNum)
{
case 2:
{
m_aPt1 = aMeasureRec.aPt1;
SetTextDirty();
break;
}
case 3:
{
m_aPt2 = aMeasureRec.aPt2;
SetTextDirty();
break;
}
default:
{
switch(nHdlNum)
{
case 0:
case 1:
{
ImpMeasureRec aOrigMeasureRec;
ImpTakeAttr(aOrigMeasureRec);
if(aMeasureRec.nHelpline1Len != aOrigMeasureRec.nHelpline1Len)
{
SetObjectItem(makeSdrMeasureHelpline1LenItem(aMeasureRec.nHelpline1Len));
}
if(aMeasureRec.nHelpline2Len != aOrigMeasureRec.nHelpline2Len)
{
SetObjectItem(makeSdrMeasureHelpline2LenItem(aMeasureRec.nHelpline2Len));
}
break;
}
case 4:
case 5:
{
ImpMeasureRec aOrigMeasureRec;
ImpTakeAttr(aOrigMeasureRec);
if(aMeasureRec.nLineDist != aOrigMeasureRec.nLineDist)
{
SetObjectItem(makeSdrMeasureLineDistItem(aMeasureRec.nLineDist));
}
if(aMeasureRec.bBelowRefEdge != aOrigMeasureRec.bBelowRefEdge)
{
SetObjectItem(SdrMeasureBelowRefEdgeItem(aMeasureRec.bBelowRefEdge));
}
}
}
}
} // switch
SetBoundAndSnapRectsDirty();
SetChanged();
return true;
}
OUString SdrMeasureObj::getSpecialDragComment(const SdrDragStat& /*rDrag*/) const
{
return OUString();
}
void SdrMeasureObj::ImpEvalDrag(ImpMeasureRec& rRec, const SdrDragStat& rDrag) const
{
Degree100 nLineAngle=GetAngle(rRec.aPt2-rRec.aPt1);
double a = toRadians(nLineAngle);
double nSin=sin(a);
double nCos=cos(a);
const SdrHdl* pHdl=rDrag.GetHdl();
sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
bool bOrtho=rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho();
bool bBigOrtho=bOrtho && rDrag.GetView()->IsBigOrtho();
bool bBelow=rRec.bBelowRefEdge;
Point aPt(rDrag.GetNow());
switch (nHdlNum) {
case 0: {
RotatePoint(aPt,m_aPt1,nSin,-nCos);
rRec.nHelpline1Len=m_aPt1.Y()-aPt.Y();
if (bBelow) rRec.nHelpline1Len=-rRec.nHelpline1Len;
if (bOrtho) rRec.nHelpline2Len=rRec.nHelpline1Len;
} break;
case 1: {
RotatePoint(aPt,m_aPt2,nSin,-nCos);
rRec.nHelpline2Len=m_aPt2.Y()-aPt.Y();
if (bBelow) rRec.nHelpline2Len=-rRec.nHelpline2Len;
if (bOrtho) rRec.nHelpline1Len=rRec.nHelpline2Len;
} break;
case 2: case 3: {
bool bAnf=nHdlNum==2;
Point& rMov=bAnf ? rRec.aPt1 : rRec.aPt2;
Point aMov(rMov);
Point aFix(bAnf ? rRec.aPt2 : rRec.aPt1);
if (bOrtho) {
tools::Long ndx0=aMov.X()-aFix.X();
tools::Long ndy0=aMov.Y()-aFix.Y();
bool bHLin=ndy0==0;
bool bVLin=ndx0==0;
if (!bHLin || !bVLin) { // else aPt1==aPt2
tools::Long ndx=aPt.X()-aFix.X();
tools::Long ndy=aPt.Y()-aFix.Y();
double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
if (bHor) ndy=tools::Long(ndy0*nXFact);
if (bVer) ndx=tools::Long(ndx0*nYFact);
aPt=aFix;
aPt.AdjustX(ndx );
aPt.AdjustY(ndy );
} // else Ortho8
}
rMov=aPt;
} break;
case 4: case 5: {
tools::Long nVal0=rRec.nLineDist;
RotatePoint(aPt,(nHdlNum==4 ? m_aPt1 : m_aPt2),nSin,-nCos);
rRec.nLineDist=aPt.Y()- (nHdlNum==4 ? m_aPt1.Y() : m_aPt2.Y());
if (bBelow) rRec.nLineDist=-rRec.nLineDist;
if (rRec.nLineDist<0) {
rRec.nLineDist=-rRec.nLineDist;
rRec.bBelowRefEdge=!bBelow;
}
rRec.nLineDist-=rRec.nHelplineOverhang;
if (bOrtho) rRec.nLineDist=nVal0;
} break;
} // switch
}
bool SdrMeasureObj::BegCreate(SdrDragStat& rStat)
{
rStat.SetOrtho8Possible();
m_aPt1=rStat.GetStart();
m_aPt2=rStat.GetNow();
SetTextDirty();
return true;
}
bool SdrMeasureObj::MovCreate(SdrDragStat& rStat)
{
SdrView* pView=rStat.GetView();
m_aPt1=rStat.GetStart();
m_aPt2=rStat.GetNow();
if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
m_aPt1+=m_aPt1;
m_aPt1-=rStat.GetNow();
}
SetTextDirty();
SetBoundRectDirty();
m_bSnapRectDirty=true;
return true;
}
bool SdrMeasureObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
{
SetTextDirty();
SetBoundAndSnapRectsDirty();
return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
}
bool SdrMeasureObj::BckCreate(SdrDragStat& /*rStat*/)
{
return false;
}
void SdrMeasureObj::BrkCreate(SdrDragStat& /*rStat*/)
{
}
basegfx::B2DPolyPolygon SdrMeasureObj::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
{
ImpMeasureRec aRec;
ImpMeasurePoly aMPol;
ImpTakeAttr(aRec);
ImpCalcGeometrics(aRec, aMPol);
return ImpCalcXPoly(aMPol);
}
PointerStyle SdrMeasureObj::GetCreatePointer() const
{
return PointerStyle::Cross;
}
void SdrMeasureObj::NbcMove(const Size& rSiz)
{
SdrTextObj::NbcMove(rSiz);
m_aPt1.Move(rSiz);
m_aPt2.Move(rSiz);
}
void SdrMeasureObj::NbcResize(const Point& rRef, double xFact, double yFact)
{
SdrTextObj::NbcResize(rRef,xFact,yFact);
ResizePoint(m_aPt1,rRef,xFact,yFact);
ResizePoint(m_aPt2,rRef,xFact,yFact);
SetTextDirty();
}
void SdrMeasureObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
{
SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
tools::Long nLen0=GetLen(m_aPt2-m_aPt1);
RotatePoint(m_aPt1,rRef,sn,cs);
RotatePoint(m_aPt2,rRef,sn,cs);
tools::Long nLen1=GetLen(m_aPt2-m_aPt1);
if (nLen1!=nLen0) { // rounding error!
tools::Long dx=m_aPt2.X()-m_aPt1.X();
tools::Long dy=m_aPt2.Y()-m_aPt1.Y();
dx=BigMulDiv(dx,nLen0,nLen1);
dy=BigMulDiv(dy,nLen0,nLen1);
if (rRef==m_aPt2) {
m_aPt1.setX(m_aPt2.X()-dx );
m_aPt1.setY(m_aPt2.Y()-dy );
} else {
m_aPt2.setX(m_aPt1.X()+dx );
m_aPt2.setY(m_aPt1.Y()+dy );
}
}
SetBoundAndSnapRectsDirty();
}
void SdrMeasureObj::NbcMirror(const Point& rRef1, const Point& rRef2)
{
SdrTextObj::NbcMirror(rRef1,rRef2);
MirrorPoint(m_aPt1,rRef1,rRef2);
MirrorPoint(m_aPt2,rRef1,rRef2);
SetBoundAndSnapRectsDirty();
}
void SdrMeasureObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
{
SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
ShearPoint(m_aPt1,rRef,tn,bVShear);
ShearPoint(m_aPt2,rRef,tn,bVShear);
SetBoundAndSnapRectsDirty();
SetTextDirty();
}
Degree100 SdrMeasureObj::GetRotateAngle() const
{
return GetAngle(m_aPt2-m_aPt1);
}
void SdrMeasureObj::RecalcSnapRect()
{
ImpMeasureRec aRec;
ImpMeasurePoly aMPol;
XPolyPolygon aXPP;
ImpTakeAttr(aRec);
ImpCalcGeometrics(aRec, aMPol);
aXPP = XPolyPolygon(ImpCalcXPoly(aMPol));
maSnapRect = aXPP.GetBoundRect();
}
sal_uInt32 SdrMeasureObj::GetSnapPointCount() const
{
return 2;
}
Point SdrMeasureObj::GetSnapPoint(sal_uInt32 i) const
{
if (i==0) return m_aPt1;
else return m_aPt2;
}
bool SdrMeasureObj::IsPolyObj() const
{
return true;
}
sal_uInt32 SdrMeasureObj::GetPointCount() const
{
return 2;
}
Point SdrMeasureObj::GetPoint(sal_uInt32 i) const
{
return (0 == i) ? m_aPt1 : m_aPt2;
}
void SdrMeasureObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
{
if (0 == i)
m_aPt1=rPnt;
if (1 == i)
m_aPt2=rPnt;
SetBoundAndSnapRectsDirty();
SetTextDirty();
}
std::unique_ptr<SdrObjGeoData> SdrMeasureObj::NewGeoData() const
{
return std::make_unique<SdrMeasureObjGeoData>();
}
void SdrMeasureObj::SaveGeoData(SdrObjGeoData& rGeo) const
{
SdrTextObj::SaveGeoData(rGeo);
SdrMeasureObjGeoData& rMGeo=static_cast<SdrMeasureObjGeoData&>(rGeo);
rMGeo.aPt1=m_aPt1;
rMGeo.aPt2=m_aPt2;
}
void SdrMeasureObj::RestoreGeoData(const SdrObjGeoData& rGeo)
{
SdrTextObj::RestoreGeoData(rGeo);
const SdrMeasureObjGeoData& rMGeo=static_cast<const SdrMeasureObjGeoData&>(rGeo);
m_aPt1=rMGeo.aPt1;
m_aPt2=rMGeo.aPt2;
SetTextDirty();
}
rtl::Reference<SdrObject> SdrMeasureObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
{
// get XOR Poly as base
XPolyPolygon aTmpPolyPolygon(TakeXorPoly());
// get local ItemSet and StyleSheet
SfxItemSet aSet(GetObjectItemSet());
SfxStyleSheet* pStyleSheet = GetStyleSheet();
// prepare group
rtl::Reference<SdrObjGroup> pGroup(new SdrObjGroup(getSdrModelFromSdrObject()));
// prepare parameters
basegfx::B2DPolyPolygon aPolyPoly;
rtl::Reference<SdrPathObj> pPath;
sal_uInt16 nCount(aTmpPolyPolygon.Count());
sal_uInt16 nLoopStart(0);
if(nCount == 3)
{
// three lines, first one is the middle one
aPolyPoly.clear();
aPolyPoly.append(aTmpPolyPolygon[0].getB2DPolygon());
pPath = new SdrPathObj(
getSdrModelFromSdrObject(),
SdrObjKind::PathLine,
aPolyPoly);
pPath->SetMergedItemSet(aSet);
pPath->SetStyleSheet(pStyleSheet, true);
pGroup->GetSubList()->NbcInsertObject(pPath.get());
aSet.Put(XLineStartWidthItem(0));
aSet.Put(XLineEndWidthItem(0));
nLoopStart = 1;
}
else if(nCount == 4)
{
// four lines, middle line with gap, so there are two lines used
// which have one arrow each
sal_Int32 nEndWidth = aSet.Get(XATTR_LINEENDWIDTH).GetValue();
aSet.Put(XLineEndWidthItem(0));
aPolyPoly.clear();
aPolyPoly.append(aTmpPolyPolygon[0].getB2DPolygon());
pPath = new SdrPathObj(
getSdrModelFromSdrObject(),
SdrObjKind::PathLine,
aPolyPoly);
pPath->SetMergedItemSet(aSet);
pPath->SetStyleSheet(pStyleSheet, true);
pGroup->GetSubList()->NbcInsertObject(pPath.get());
aSet.Put(XLineEndWidthItem(nEndWidth));
aSet.Put(XLineStartWidthItem(0));
aPolyPoly.clear();
aPolyPoly.append(aTmpPolyPolygon[1].getB2DPolygon());
pPath = new SdrPathObj(
getSdrModelFromSdrObject(),
SdrObjKind::PathLine,
aPolyPoly);
pPath->SetMergedItemSet(aSet);
pPath->SetStyleSheet(pStyleSheet, true);
pGroup->GetSubList()->NbcInsertObject(pPath.get());
aSet.Put(XLineEndWidthItem(0));
nLoopStart = 2;
}
else if(nCount == 5)
{
// five lines, first two are the outer ones
sal_Int32 nEndWidth = aSet.Get(XATTR_LINEENDWIDTH).GetValue();
aSet.Put(XLineEndWidthItem(0));
aPolyPoly.clear();
aPolyPoly.append(aTmpPolyPolygon[0].getB2DPolygon());
pPath = new SdrPathObj(
getSdrModelFromSdrObject(),
SdrObjKind::PathLine,
aPolyPoly);
pPath->SetMergedItemSet(aSet);
pPath->SetStyleSheet(pStyleSheet, true);
pGroup->GetSubList()->NbcInsertObject(pPath.get());
aSet.Put(XLineEndWidthItem(nEndWidth));
aSet.Put(XLineStartWidthItem(0));
aPolyPoly.clear();
aPolyPoly.append(aTmpPolyPolygon[1].getB2DPolygon());
pPath = new SdrPathObj(
getSdrModelFromSdrObject(),
SdrObjKind::PathLine,
aPolyPoly);
pPath->SetMergedItemSet(aSet);
pPath->SetStyleSheet(pStyleSheet, true);
pGroup->GetSubList()->NbcInsertObject(pPath.get());
aSet.Put(XLineEndWidthItem(0));
nLoopStart = 2;
}
for(;nLoopStart<nCount;nLoopStart++)
{
aPolyPoly.clear();
aPolyPoly.append(aTmpPolyPolygon[nLoopStart].getB2DPolygon());
pPath = new SdrPathObj(
getSdrModelFromSdrObject(),
SdrObjKind::PathLine,
aPolyPoly);
pPath->SetMergedItemSet(aSet);
pPath->SetStyleSheet(pStyleSheet, true);
pGroup->GetSubList()->NbcInsertObject(pPath.get());
}
if(bAddText)
{
return ImpConvertAddText(std::move(pGroup), bBezier);
}
else
{
return pGroup;
}
}
bool SdrMeasureObj::BegTextEdit(SdrOutliner& rOutl)
{
UndirtyText();
return SdrTextObj::BegTextEdit(rOutl);
}
const Size& SdrMeasureObj::GetTextSize() const
{
if (m_bTextDirty) UndirtyText();
return SdrTextObj::GetTextSize();
}
OutlinerParaObject* SdrMeasureObj::GetOutlinerParaObject() const
{
if(m_bTextDirty)
UndirtyText();
return SdrTextObj::GetOutlinerParaObject();
}
void SdrMeasureObj::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject, bool bAdjustTextFrameWidthAndHeight)
{
SdrTextObj::NbcSetOutlinerParaObject(std::move(pTextObject), bAdjustTextFrameWidthAndHeight);
if(SdrTextObj::GetOutlinerParaObject())
SetTextDirty(); // recalculate text
}
void SdrMeasureObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
tools::Rectangle* pAnchorRect, bool bLineWidth ) const
{
if (m_bTextDirty) UndirtyText();
SdrTextObj::TakeTextRect( rOutliner, rTextRect, bNoEditText, pAnchorRect, bLineWidth );
}
void SdrMeasureObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
{
if (m_bTextDirty) UndirtyText();
SdrTextObj::TakeTextAnchorRect(rAnchorRect);
}
void SdrMeasureObj::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
{
if (m_bTextDirty) UndirtyText();
SdrTextObj::TakeTextEditArea(pPaperMin,pPaperMax,pViewInit,pViewMin);
}
EEAnchorMode SdrMeasureObj::GetOutlinerViewAnchorMode() const
{
if (m_bTextDirty) UndirtyText();
ImpMeasureRec aRec;
ImpMeasurePoly aMPol;
ImpTakeAttr(aRec);
ImpCalcGeometrics(aRec,aMPol);
SdrTextHorzAdjust eTH=GetTextHorizontalAdjust();
SdrTextVertAdjust eTV=GetTextVerticalAdjust();
css::drawing::MeasureTextHorzPos eMH = aMPol.eUsedTextHPos;
css::drawing::MeasureTextVertPos eMV = aMPol.eUsedTextVPos;
bool bTextRota90=aRec.bTextRota90;
bool bBelowRefEdge=aRec.bBelowRefEdge;
// TODO: bTextUpsideDown should be interpreted here!
if (!bTextRota90) {
if (eMH==css::drawing::MeasureTextHorzPos_LEFTOUTSIDE) eTH=SDRTEXTHORZADJUST_RIGHT;
if (eMH==css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE) eTH=SDRTEXTHORZADJUST_LEFT;
// at eMH==css::drawing::MeasureTextHorzPos_INSIDE we can anchor horizontally
if (eMV==css::drawing::MeasureTextVertPos_EAST) eTV=SDRTEXTVERTADJUST_BOTTOM;
if (eMV==css::drawing::MeasureTextVertPos_WEST) eTV=SDRTEXTVERTADJUST_TOP;
if (eMV==css::drawing::MeasureTextVertPos_CENTERED) eTV=SDRTEXTVERTADJUST_CENTER;
} else {
if (eMH==css::drawing::MeasureTextHorzPos_LEFTOUTSIDE) eTV=SDRTEXTVERTADJUST_BOTTOM;
if (eMH==css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE) eTV=SDRTEXTVERTADJUST_TOP;
// at eMH==css::drawing::MeasureTextHorzPos_INSIDE we can anchor vertically
if (!bBelowRefEdge) {
if (eMV==css::drawing::MeasureTextVertPos_EAST) eTH=SDRTEXTHORZADJUST_LEFT;
if (eMV==css::drawing::MeasureTextVertPos_WEST) eTH=SDRTEXTHORZADJUST_RIGHT;
} else {
if (eMV==css::drawing::MeasureTextVertPos_EAST) eTH=SDRTEXTHORZADJUST_RIGHT;
if (eMV==css::drawing::MeasureTextVertPos_WEST) eTH=SDRTEXTHORZADJUST_LEFT;
}
if (eMV==css::drawing::MeasureTextVertPos_CENTERED) eTH=SDRTEXTHORZADJUST_CENTER;
}
EEAnchorMode eRet=EEAnchorMode::BottomHCenter;
if (eTH==SDRTEXTHORZADJUST_LEFT) {
if (eTV==SDRTEXTVERTADJUST_TOP) eRet=EEAnchorMode::TopLeft;
else if (eTV==SDRTEXTVERTADJUST_BOTTOM) eRet=EEAnchorMode::BottomLeft;
else eRet=EEAnchorMode::VCenterLeft;
} else if (eTH==SDRTEXTHORZADJUST_RIGHT) {
if (eTV==SDRTEXTVERTADJUST_TOP) eRet=EEAnchorMode::TopRight;
else if (eTV==SDRTEXTVERTADJUST_BOTTOM) eRet=EEAnchorMode::BottomRight;
else eRet=EEAnchorMode::VCenterRight;
} else {
if (eTV==SDRTEXTVERTADJUST_TOP) eRet=EEAnchorMode::TopHCenter;
else if (eTV==SDRTEXTVERTADJUST_BOTTOM) eRet=EEAnchorMode::BottomHCenter;
else eRet=EEAnchorMode::VCenterHCenter;
}
return eRet;
}
// #i97878#
// TRGetBaseGeometry/TRSetBaseGeometry needs to be based on two positions,
// same as line geometry in SdrPathObj. Thus needs to be overridden and
// implemented since currently it is derived from SdrTextObj which uses
// a functionality based on SnapRect which is not useful here
bool SdrMeasureObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
{
// handle the same as a simple line since the definition is based on two points
const basegfx::B2DRange aRange(m_aPt1.X(), m_aPt1.Y(), m_aPt2.X(), m_aPt2.Y());
basegfx::B2DTuple aScale(aRange.getRange());
basegfx::B2DTuple aTranslate(aRange.getMinimum());
// position maybe relative to anchor position, convert
if( getSdrModelFromSdrObject().IsWriter() )
{
if(GetAnchorPos().X() || GetAnchorPos().Y())
{
aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
}
}
// build return value matrix
rMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(aScale, aTranslate);
return true;
}
void SdrMeasureObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
{
// use given transformation to derive the two defining points from unit line
basegfx::B2DPoint aPosA(rMatrix * basegfx::B2DPoint(0.0, 0.0));
basegfx::B2DPoint aPosB(rMatrix * basegfx::B2DPoint(1.0, 0.0));
if( getSdrModelFromSdrObject().IsWriter() )
{
// if anchor is used, make position relative to it
if(GetAnchorPos().X() || GetAnchorPos().Y())
{
const basegfx::B2DVector aAnchorOffset(GetAnchorPos().X(), GetAnchorPos().Y());
aPosA += aAnchorOffset;
aPosB += aAnchorOffset;
}
}
// derive new model data
const Point aNewPt1(basegfx::fround<tools::Long>(aPosA.getX()), basegfx::fround<tools::Long>(aPosA.getY()));
const Point aNewPt2(basegfx::fround<tools::Long>(aPosB.getX()), basegfx::fround<tools::Long>(aPosB.getY()));
if(aNewPt1 == m_aPt1 && aNewPt2 == m_aPt2)
return;
// set model values and broadcast
tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
m_aPt1 = aNewPt1;
m_aPt2 = aNewPt2;
SetTextDirty();
ActionChanged();
SetChanged();
BroadcastObjectChange();
SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */