/* -*- 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/outliner.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/svdpool.hxx>
#include <svx/svdtrans.hxx>
#include <svx/svdview.hxx>
#include <svx/sxmbritm.hxx>
#include <svx/sxmfsitm.hxx>
#include <sxmkitm.hxx>
#include <svx/sxmlhitm.hxx>
#include <sxmoitm.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 <o3tl/make_unique.hxx>
 
 
SdrMeasureObjGeoData::SdrMeasureObjGeoData() {}
SdrMeasureObjGeoData::~SdrMeasureObjGeoData() {}
 
OUString SdrMeasureObj::TakeRepresentation(SdrMeasureFieldKind eMeasureFieldKind) const
{
    OUString aStr;
    Fraction aMeasureScale(1, 1);
    bool bTextRota90(false);
    bool bShowUnit(false);
    FieldUnit eMeasureUnit(FUNIT_NONE);
    FieldUnit eModUIUnit(FUNIT_NONE);
 
    const SfxItemSet& rSet = GetMergedItemSet();
    bTextRota90 = rSet.Get(SDRATTR_MEASURETEXTROTA90).GetValue();
    eMeasureUnit = rSet.Get(SDRATTR_MEASUREUNIT).GetValue();
    aMeasureScale = 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 == FUNIT_NONE)
                eMeasureUnit = eModUIUnit;
 
            sal_Int32 nLen(GetLen(aPt2 - aPt1));
            Fraction aFact(1,1);
 
            if(eMeasureUnit != eModUIUnit)
            {
                // for the unit conversion
                aFact *= GetMapFactor(eModUIUnit, eMeasureUnit).X();
            }
 
            if(aMeasureScale.GetNumerator() != aMeasureScale.GetDenominator())
            {
                aFact *= aMeasureScale;
            }
 
            if(aFact.GetNumerator() != aFact.GetDenominator())
            {
                // scale via BigInt, to avoid overruns
                nLen = BigMulDiv(nLen, aFact.GetNumerator(), aFact.GetDenominator());
            }
 
            if(!aFact.IsValid())
            {
                aStr = "?";
            }
            else
            {
                aStr = getSdrModelFromSdrObject().GetMetricString(nLen, true, nNumDigits);
            }
 
            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 == FUNIT_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 o3tl::make_unique<sdr::properties::MeasureProperties>(*this);
}
 
 
// DrawContact section
 
std::unique_ptr<sdr::contact::ViewContact> SdrMeasureObj::CreateObjectSpecificViewContact()
{
    return o3tl::make_unique<sdr::contact::ViewContactOfSdrMeasureObj>(*this);
}
 
 
SdrMeasureObj::SdrMeasureObj(SdrModel& rSdrModel)
:   SdrTextObj(rSdrModel),
    bTextDirty(false)
{
    // #i25616#
    mbSupportTextIndentingOnLineWidthChange = false;
}
 
SdrMeasureObj::SdrMeasureObj(
    SdrModel& rSdrModel,
    const Point& rPt1,
    const Point& rPt2)
:   SdrTextObj(rSdrModel),
    aPt1(rPt1),
    aPt2(rPt2),
    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 = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
}
 
sal_uInt16 SdrMeasureObj::GetObjIdentifier() const
{
    return sal_uInt16(OBJ_MEASURE);
}
 
struct ImpMeasureRec : public SdrDragStatUserData
{
    Point                       aPt1;
    Point                       aPt2;
    css::drawing::MeasureTextHorzPos eWantTextHPos;
    css::drawing::MeasureTextVertPos eWantTextVPos;
    long                        nLineDist;
    long                        nHelplineOverhang;
    long                        nHelplineDist;
    long                        nHelpline1Len;
    long                        nHelpline2Len;
    bool                        bBelowRefEdge;
    bool                        bTextRota90;
    bool                        bTextUpsideDown;
    Fraction                    aMeasureScale;
    OUString                    aFormatString;
    bool                        bTextAutoAngle;
    long                        nTextAutoAngleView;
};
 
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;
    long                        nLineLen;
    long                        nLineAngle;
    long                        nTextAngle;
    long                        nHlpAngle;
    double                      nLineSin;
    double                      nLineCos;
    sal_uInt16                      nMainlineCnt;
    css::drawing::MeasureTextHorzPos eUsedTextHPos;
    css::drawing::MeasureTextVertPos eUsedTextVPos;
    long                        nLineWdt2;  // half the line width
    long                        nArrow1Len; // length of 1st arrowhead; for Center, use only half
    long                        nArrow2Len; // length of 2nd arrowhead; for Center, use only half
    long                        nArrow1Wdt; // width of 1st arrow
    long                        nArrow2Wdt; // width of 2nd arrow
    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 = aPt1;
    rRec.aPt2 = 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.aMeasureScale     =rSet.Get(SDRATTR_MEASURESCALE           ).GetValue();
    rRec.aFormatString     =rSet.Get(SDRATTR_MEASUREFORMATSTRING    ).GetValue();
    rRec.bTextAutoAngle    =rSet.Get(SDRATTR_MEASURETEXTAUTOANGLE    ).GetValue();
    rRec.nTextAutoAngleView=static_cast<const SdrMeasureTextAutoAngleViewItem&>(rSet.Get(SDRATTR_MEASURETEXTAUTOANGLEVIEW)).GetValue();
}
 
long impGetLineStartEndDistance(const basegfx::B2DPolyPolygon& rPolyPolygon, 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);
    long nHeight(basegfx::fround(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;
    long nArrow1Len=0; bool bArrow1Center=false;
    long nArrow2Len=0; bool bArrow2Center=false;
    long nArrow1Wdt=0;
    long nArrow2Wdt=0;
    rPol.nArrow1Wdt=0;
    rPol.nArrow2Wdt=0;
    long nArrowNeed=0;
    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;
        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 {
            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=rPol.nLineAngle*nPi180;
    double nLineSin=sin(a);
    double nLineCos=cos(a);
    rPol.nLineSin=nLineSin;
    rPol.nLineCos=nLineCos;
 
    rPol.nTextAngle=rPol.nLineAngle;
    if (rRec.bTextRota90) rPol.nTextAngle+=9000;
 
    rPol.bAutoUpsideDown=false;
    if (rRec.bTextAutoAngle) {
        long nTmpAngle=NormAngle36000(rPol.nTextAngle-rRec.nTextAutoAngleView);
        if (nTmpAngle>=18000) {
            rPol.nTextAngle+=18000;
            rPol.bAutoUpsideDown=true;
        }
    }
 
    if (rRec.bTextUpsideDown) rPol.nTextAngle+=18000;
    rPol.nTextAngle=NormAngle36000(rPol.nTextAngle);
    rPol.nHlpAngle=rPol.nLineAngle+9000;
    if (rRec.bBelowRefEdge) rPol.nHlpAngle+=18000;
    rPol.nHlpAngle=NormAngle36000(rPol.nHlpAngle);
    double nHlpSin=nLineCos;
    double nHlpCos=-nLineSin;
    if (rRec.bBelowRefEdge) {
        nHlpSin=-nHlpSin;
        nHlpCos=-nHlpCos;
    }
 
    long nLineDist=rRec.nLineDist;
    long nOverhang=rRec.nHelplineOverhang;
    long nHelplineDist=rRec.nHelplineDist;
 
    long dx= FRound(nLineDist*nHlpCos);
    long dy=-FRound(nLineDist*nHlpSin);
    long dxh1a= FRound((nHelplineDist-rRec.nHelpline1Len)*nHlpCos);
    long dyh1a=-FRound((nHelplineDist-rRec.nHelpline1Len)*nHlpSin);
    long dxh1b= FRound((nHelplineDist-rRec.nHelpline2Len)*nHlpCos);
    long dyh1b=-FRound((nHelplineDist-rRec.nHelpline2Len)*nHlpSin);
    long dxh2= FRound((nLineDist+nOverhang)*nHlpCos);
    long dyh2=-FRound((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) {
            long nNeedSiz=!rRec.bTextRota90 ? rPol.aTextSize.Width() : rPol.aTextSize.Height();
            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 {
        long nLen1=nShortLen; // arrowhead's width as line length outside of the arrowhead
        long nLen2=nShortLen;
        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,
    boost::optional<Color>& rpTxtColor, boost::optional<Color>& rpFldColor, 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,rRet);
    }
}
 
void SdrMeasureObj::UndirtyText() const
{
    if (bTextDirty)
    {
        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(" ", 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.SetUpdateMode(true);
        rOutliner.UpdateFields();
        Size aSiz(rOutliner.CalcTextSize());
        rOutliner.Clear();
        // cast to nonconst three times
        const_cast<SdrMeasureObj*>(this)->aTextSize=aSiz;
        const_cast<SdrMeasureObj*>(this)->bTextSizeDirty=false;
        const_cast<SdrMeasureObj*>(this)->bTextDirty=false;
    }
}
 
void SdrMeasureObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
{
    if (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);
    long nLen=aMPol.nLineLen;
    long nLWdt=aMPol.nLineWdt2;
    long nArr1Len=aMPol.nArrow1Len;
    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!=aGeo.nRotationAngle) {
        const_cast<SdrMeasureObj*>(this)->aGeo.nRotationAngle=aMPol.nTextAngle;
        const_cast<SdrMeasureObj*>(this)->aGeo.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.Justify();
    const_cast<SdrMeasureObj*>(this)->maRect=rRect;
 
    if (aMPol.nTextAngle!=aGeo.nRotationAngle) {
        const_cast<SdrMeasureObj*>(this)->aGeo.nRotationAngle=aMPol.nTextAngle;
        const_cast<SdrMeasureObj*>(this)->aGeo.RecalcSinCos();
    }
}
 
SdrMeasureObj* SdrMeasureObj::CloneSdrObject(SdrModel& rTargetModel) const
{
    return CloneHelper< SdrMeasureObj >(rTargetModel);
}
 
SdrMeasureObj& SdrMeasureObj::operator=(const SdrMeasureObj& rObj)
{
    if( this == &rObj )
        return *this;
    SdrTextObj::operator=(rObj);
 
    aPt1 = rObj.aPt1;
    aPt2 = rObj.aPt2;
    bTextDirty = rObj.bTextDirty;
 
    return *this;
}
 
OUString SdrMeasureObj::TakeObjNameSingul() const
{
    OUStringBuffer sName(SvxResId(STR_ObjNameSingulMEASURE));
 
    OUString aName( GetName() );
    if (!aName.isEmpty())
    {
        sName.append(' ');
        sName.append('\'');
        sName.append(aName);
        sName.append('\'');
    }
 
    return sName.makeStringAndClear();
}
 
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 6L;
}
 
SdrHdl* SdrMeasureObj::GetHdl(sal_uInt32 nHdlNum) const
{
    ImpMeasureRec aRec;
    ImpMeasurePoly aMPol;
    ImpTakeAttr(aRec);
    aRec.nHelplineDist=0;
    ImpCalcGeometrics(aRec,aMPol);
    Point aPt;
 
    switch (nHdlNum) {
        case 0: aPt=aMPol.aHelpline1.aP1; break;
        case 1: aPt=aMPol.aHelpline2.aP1; break;
        case 2: aPt=aPt1;       break;
        case 3: aPt=aPt2;       break;
        case 4: aPt=aMPol.aHelpline1.aP2; break;
        case 5: aPt=aMPol.aHelpline2.aP2; break;
    } // switch
    SdrHdl* pHdl=new ImpMeasureHdl(aPt,SdrHdlKind::User);
    pHdl->SetObjHdlNum(nHdlNum);
    pHdl->SetRotationAngle(aMPol.nLineAngle);
    return 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:
        {
            aPt1 = aMeasureRec.aPt1;
            SetTextDirty();
            break;
        }
        case 3:
        {
            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
 
    SetRectsDirty();
    SetChanged();
 
    return true;
}
 
OUString SdrMeasureObj::getSpecialDragComment(const SdrDragStat& /*rDrag*/) const
{
    return OUString();
}
 
void SdrMeasureObj::ImpEvalDrag(ImpMeasureRec& rRec, const SdrDragStat& rDrag) const
{
    long nLineAngle=GetAngle(rRec.aPt2-rRec.aPt1);
    double a=nLineAngle*nPi180;
    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,aPt1,nSin,-nCos);
            rRec.nHelpline1Len=aPt1.Y()-aPt.Y();
            if (bBelow) rRec.nHelpline1Len=-rRec.nHelpline1Len;
            if (bOrtho) rRec.nHelpline2Len=rRec.nHelpline1Len;
        } break;
        case 1: {
            RotatePoint(aPt,aPt2,nSin,-nCos);
            rRec.nHelpline2Len=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) {
                long ndx0=aMov.X()-aFix.X();
                long ndy0=aMov.Y()-aFix.Y();
                bool bHLin=ndy0==0;
                bool bVLin=ndx0==0;
                if (!bHLin || !bVLin) { // else aPt1==aPt2
                    long ndx=aPt.X()-aFix.X();
                    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=long(ndy0*nXFact);
                    if (bVer) ndx=long(ndx0*nYFact);
                    aPt=aFix;
                    aPt.AdjustX(ndx );
                    aPt.AdjustY(ndy );
                } // else Ortho8
            }
            rMov=aPt;
        } break;
        case 4: case 5: {
            long nVal0=rRec.nLineDist;
            RotatePoint(aPt,(nHdlNum==4 ? aPt1 : aPt2),nSin,-nCos);
            rRec.nLineDist=aPt.Y()- (nHdlNum==4 ? aPt1.Y() : 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();
    aPt1=rStat.GetStart();
    aPt2=rStat.GetNow();
    SetTextDirty();
    return true;
}
 
bool SdrMeasureObj::MovCreate(SdrDragStat& rStat)
{
    SdrView* pView=rStat.GetView();
    aPt1=rStat.GetStart();
    aPt2=rStat.GetNow();
    if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
        aPt1+=aPt1;
        aPt1-=rStat.GetNow();
    }
    SetTextDirty();
    SetBoundRectDirty();
    bSnapRectDirty=true;
    return true;
}
 
bool SdrMeasureObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
{
    SetTextDirty();
    SetRectsDirty();
    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);
}
 
Pointer SdrMeasureObj::GetCreatePointer() const
{
    return Pointer(PointerStyle::Cross);
}
 
void SdrMeasureObj::NbcMove(const Size& rSiz)
{
    SdrTextObj::NbcMove(rSiz);
    aPt1.Move(rSiz);
    aPt2.Move(rSiz);
}
 
void SdrMeasureObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
{
    SdrTextObj::NbcResize(rRef,xFact,yFact);
    ResizePoint(aPt1,rRef,xFact,yFact);
    ResizePoint(aPt2,rRef,xFact,yFact);
    SetTextDirty();
}
 
void SdrMeasureObj::NbcRotate(const Point& rRef, long nAngle, double sn, double cs)
{
    SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
    long nLen0=GetLen(aPt2-aPt1);
    RotatePoint(aPt1,rRef,sn,cs);
    RotatePoint(aPt2,rRef,sn,cs);
    long nLen1=GetLen(aPt2-aPt1);
    if (nLen1!=nLen0) { // rounding error!
        long dx=aPt2.X()-aPt1.X();
        long dy=aPt2.Y()-aPt1.Y();
        dx=BigMulDiv(dx,nLen0,nLen1);
        dy=BigMulDiv(dy,nLen0,nLen1);
        if (rRef==aPt2) {
            aPt1.setX(aPt2.X()-dx );
            aPt1.setY(aPt2.Y()-dy );
        } else {
            aPt2.setX(aPt1.X()+dx );
            aPt2.setY(aPt1.Y()+dy );
        }
    }
    SetRectsDirty();
}
 
void SdrMeasureObj::NbcMirror(const Point& rRef1, const Point& rRef2)
{
    SdrTextObj::NbcMirror(rRef1,rRef2);
    MirrorPoint(aPt1,rRef1,rRef2);
    MirrorPoint(aPt2,rRef1,rRef2);
    SetRectsDirty();
}
 
void SdrMeasureObj::NbcShear(const Point& rRef, long nAngle, double tn, bool bVShear)
{
    SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
    ShearPoint(aPt1,rRef,tn,bVShear);
    ShearPoint(aPt2,rRef,tn,bVShear);
    SetRectsDirty();
    SetTextDirty();
}
 
long SdrMeasureObj::GetRotateAngle() const
{
    return GetAngle(aPt2-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 2L;
}
 
Point SdrMeasureObj::GetSnapPoint(sal_uInt32 i) const
{
    if (i==0) return aPt1;
    else return aPt2;
}
 
bool SdrMeasureObj::IsPolyObj() const
{
    return true;
}
 
sal_uInt32 SdrMeasureObj::GetPointCount() const
{
    return 2L;
}
 
Point SdrMeasureObj::GetPoint(sal_uInt32 i) const
{
     return (0 == i) ? aPt1 : aPt2;
}
 
void SdrMeasureObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
{
    if (0 == i)
        aPt1=rPnt;
    if (1 == i)
        aPt2=rPnt;
    SetRectsDirty();
    SetTextDirty();
}
 
SdrObjGeoData* SdrMeasureObj::NewGeoData() const
{
    return new SdrMeasureObjGeoData;
}
 
void SdrMeasureObj::SaveGeoData(SdrObjGeoData& rGeo) const
{
    SdrTextObj::SaveGeoData(rGeo);
    SdrMeasureObjGeoData& rMGeo=static_cast<SdrMeasureObjGeoData&>(rGeo);
    rMGeo.aPt1=aPt1;
    rMGeo.aPt2=aPt2;
}
 
void SdrMeasureObj::RestGeoData(const SdrObjGeoData& rGeo)
{
    SdrTextObj::RestGeoData(rGeo);
    const SdrMeasureObjGeoData& rMGeo=static_cast<const SdrMeasureObjGeoData&>(rGeo);
    aPt1=rMGeo.aPt1;
    aPt2=rMGeo.aPt2;
    SetTextDirty();
}
 
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
    SdrObjGroup* pGroup = new SdrObjGroup(getSdrModelFromSdrObject());
 
    // prepare parameters
    basegfx::B2DPolyPolygon aPolyPoly;
    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(),
            OBJ_PATHLINE,
            aPolyPoly);
 
        pPath->SetMergedItemSet(aSet);
        pPath->SetStyleSheet(pStyleSheet, true);
        pGroup->GetSubList()->NbcInsertObject(pPath);
        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(),
            OBJ_PATHLINE,
            aPolyPoly);
 
        pPath->SetMergedItemSet(aSet);
        pPath->SetStyleSheet(pStyleSheet, true);
 
        pGroup->GetSubList()->NbcInsertObject(pPath);
 
        aSet.Put(XLineEndWidthItem(nEndWidth));
        aSet.Put(XLineStartWidthItem(0));
 
        aPolyPoly.clear();
        aPolyPoly.append(aTmpPolyPolygon[1].getB2DPolygon());
        pPath = new SdrPathObj(
            getSdrModelFromSdrObject(),
            OBJ_PATHLINE,
            aPolyPoly);
 
        pPath->SetMergedItemSet(aSet);
        pPath->SetStyleSheet(pStyleSheet, true);
 
        pGroup->GetSubList()->NbcInsertObject(pPath);
 
        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(),
            OBJ_PATHLINE,
            aPolyPoly);
 
        pPath->SetMergedItemSet(aSet);
        pPath->SetStyleSheet(pStyleSheet, true);
 
        pGroup->GetSubList()->NbcInsertObject(pPath);
 
        aSet.Put(XLineEndWidthItem(nEndWidth));
        aSet.Put(XLineStartWidthItem(0));
 
        aPolyPoly.clear();
        aPolyPoly.append(aTmpPolyPolygon[1].getB2DPolygon());
        pPath = new SdrPathObj(
            getSdrModelFromSdrObject(),
            OBJ_PATHLINE,
            aPolyPoly);
 
        pPath->SetMergedItemSet(aSet);
        pPath->SetStyleSheet(pStyleSheet, true);
 
        pGroup->GetSubList()->NbcInsertObject(pPath);
 
        aSet.Put(XLineEndWidthItem(0));
        nLoopStart = 2;
    }
 
    for(;nLoopStart<nCount;nLoopStart++)
    {
        aPolyPoly.clear();
        aPolyPoly.append(aTmpPolyPolygon[nLoopStart].getB2DPolygon());
        pPath = new SdrPathObj(
            getSdrModelFromSdrObject(),
            OBJ_PATHLINE,
            aPolyPoly);
 
        pPath->SetMergedItemSet(aSet);
        pPath->SetStyleSheet(pStyleSheet, true);
 
        pGroup->GetSubList()->NbcInsertObject(pPath);
    }
 
    if(bAddText)
    {
        return ImpConvertAddText(pGroup, bBezier);
    }
    else
    {
        return pGroup;
    }
}
 
bool SdrMeasureObj::BegTextEdit(SdrOutliner& rOutl)
{
    UndirtyText();
    return SdrTextObj::BegTextEdit(rOutl);
}
 
const Size& SdrMeasureObj::GetTextSize() const
{
    if (bTextDirty) UndirtyText();
    return SdrTextObj::GetTextSize();
}
 
OutlinerParaObject* SdrMeasureObj::GetOutlinerParaObject() const
{
    if(bTextDirty)
        UndirtyText();
    return SdrTextObj::GetOutlinerParaObject();
}
 
void SdrMeasureObj::NbcSetOutlinerParaObject(std::unique_ptr<OutlinerParaObject> pTextObject)
{
    SdrTextObj::NbcSetOutlinerParaObject(std::move(pTextObject));
    if(SdrTextObj::GetOutlinerParaObject())
        SetTextDirty(); // recalculate text
}
 
void SdrMeasureObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
    tools::Rectangle* pAnchorRect, bool bLineWidth ) const
{
    if (bTextDirty) UndirtyText();
    SdrTextObj::TakeTextRect( rOutliner, rTextRect, bNoEditText, pAnchorRect, bLineWidth );
}
 
void SdrMeasureObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
{
    if (bTextDirty) UndirtyText();
    SdrTextObj::TakeTextAnchorRect(rAnchorRect);
}
 
void SdrMeasureObj::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
{
    if (bTextDirty) UndirtyText();
    SdrTextObj::TakeTextEditArea(pPaperMin,pPaperMax,pViewInit,pViewMin);
}
 
EEAnchorMode SdrMeasureObj::GetOutlinerViewAnchorMode() const
{
    if (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(aPt1.X(), aPt1.Y(), aPt2.X(), 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(aPosA.getX()), basegfx::fround(aPosA.getY()));
    const Point aNewPt2(basegfx::fround(aPosB.getX()), basegfx::fround(aPosB.getY()));
 
    if(aNewPt1 != aPt1 || aNewPt2 != aPt2)
    {
        // set model values and broadcast
        tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
 
        aPt1 = aNewPt1;
        aPt2 = aNewPt2;
 
        SetTextDirty();
        ActionChanged();
        SetChanged();
        BroadcastObjectChange();
        SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V560 A part of conditional expression is always true: rInfo.bCanConvToPoly.