Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/svdraw/svdopath.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <o3tl/unit_conversion.hxx>
21
#include <tools/bigint.hxx>
22
#include <tools/helpers.hxx>
23
#include <svx/svdopath.hxx>
24
#include <math.h>
25
#include <svx/xpoly.hxx>
26
#include <svx/svdtrans.hxx>
27
#include <svx/svddrag.hxx>
28
#include <svx/svdmodel.hxx>
29
#include <svx/svdhdl.hxx>
30
#include <svx/svdview.hxx>
31
#include <svx/dialmgr.hxx>
32
#include <svx/strings.hrc>
33
34
#include <svx/polypolygoneditor.hxx>
35
#include <sdr/contact/viewcontactofsdrpathobj.hxx>
36
#include <basegfx/matrix/b2dhommatrix.hxx>
37
#include <basegfx/point/b2dpoint.hxx>
38
#include <basegfx/polygon/b2dpolypolygontools.hxx>
39
#include <basegfx/range/b2drange.hxx>
40
#include <basegfx/curve/b2dcubicbezier.hxx>
41
#include <basegfx/polygon/b2dpolygontools.hxx>
42
#include <sdr/attribute/sdrtextattribute.hxx>
43
#include <sdr/primitive2d/sdrattributecreator.hxx>
44
#include <basegfx/matrix/b2dhommatrixtools.hxx>
45
#include <sdr/attribute/sdrformtextattribute.hxx>
46
#include <utility>
47
#include <vcl/ptrstyle.hxx>
48
#include <memory>
49
#include <sal/log.hxx>
50
#include <osl/diagnose.h>
51
52
using namespace sdr;
53
54
static sal_uInt16 GetPrevPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
55
0
{
56
0
    if (nPnt>0) {
57
0
        nPnt--;
58
0
    } else {
59
0
        nPnt=nPntMax;
60
0
        if (bClosed) nPnt--;
61
0
    }
62
0
    return nPnt;
63
0
}
64
65
static sal_uInt16 GetNextPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
66
0
{
67
0
    nPnt++;
68
0
    if (nPnt>nPntMax || (bClosed && nPnt>=nPntMax)) nPnt=0;
69
0
    return nPnt;
70
0
}
71
72
namespace {
73
74
struct ImpSdrPathDragData  : public SdrDragStatUserData
75
{
76
    XPolygon                    aXP;            // section of the original polygon
77
    bool                        bValid;         // FALSE = too few points
78
    bool                        bClosed;        // closed object?
79
    sal_uInt16                  nPoly;          // number of the polygon in the PolyPolygon
80
    sal_uInt16                  nPnt;           // number of point in the above polygon
81
    sal_uInt16                  nPointCount;    // number of points of the polygon
82
    bool                        bBegPnt;        // dragged point is first point of a Polyline
83
    bool                        bEndPnt;        // dragged point is finishing point of a Polyline
84
    sal_uInt16                  nPrevPnt;       // index of previous point
85
    sal_uInt16                  nNextPnt;       // index of next point
86
    bool                        bPrevIsBegPnt;  // previous point is first point of a Polyline
87
    bool                        bNextIsEndPnt;  // next point is first point of a Polyline
88
    sal_uInt16                  nPrevPrevPnt;   // index of point before previous point
89
    sal_uInt16                  nNextNextPnt;   // index of point after next point
90
    bool                        bControl;       // point is a control point
91
    bool                        bIsNextControl; // point is a control point after a support point
92
    bool                        bPrevIsControl; // if nPnt is a support point: a control point comes before
93
    bool                        bNextIsControl; // if nPnt is a support point: a control point comes after
94
    sal_uInt16                  nPrevPrevPnt0;
95
    sal_uInt16                  nPrevPnt0;
96
    sal_uInt16                  nPnt0;
97
    sal_uInt16                  nNextPnt0;
98
    sal_uInt16                  nNextNextPnt0;
99
    bool                        bEliminate;     // delete point? (is set by MovDrag)
100
101
    bool                        mbMultiPointDrag;
102
    const XPolyPolygon          maOrig;
103
    XPolyPolygon                maMove;
104
    std::vector<SdrHdl*>        maHandles;
105
106
public:
107
    ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag);
108
    void ResetPoly(const SdrPathObj& rPO);
109
0
    bool IsMultiPointDrag() const { return mbMultiPointDrag; }
110
};
111
112
}
113
114
ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag)
115
0
    : aXP(5)
116
0
    , bValid(false)
117
0
    , bClosed(false)
118
0
    , nPoly(0)
119
0
    , nPnt(0)
120
0
    , nPointCount(0)
121
0
    , bBegPnt(false)
122
0
    , bEndPnt(false)
123
0
    , nPrevPnt(0)
124
0
    , nNextPnt(0)
125
0
    , bPrevIsBegPnt(false)
126
0
    , bNextIsEndPnt(false)
127
0
    , nPrevPrevPnt(0)
128
0
    , nNextNextPnt(0)
129
0
    , bControl(false)
130
0
    , bIsNextControl(false)
131
0
    , bPrevIsControl(false)
132
0
    , bNextIsControl(false)
133
0
    , nPrevPrevPnt0(0)
134
0
    , nPrevPnt0(0)
135
0
    , nPnt0(0)
136
0
    , nNextPnt0(0)
137
0
    , nNextNextPnt0(0)
138
0
    , bEliminate(false)
139
0
    , mbMultiPointDrag(bMuPoDr)
140
0
    , maOrig(rPO.GetPathPoly())
141
0
    , maHandles(0)
142
0
{
143
0
    if(mbMultiPointDrag)
144
0
    {
145
0
        const SdrMarkView& rMarkView = *rDrag.GetView();
146
0
        const SdrHdlList& rHdlList = rMarkView.GetHdlList();
147
0
        const size_t nHdlCount = rHdlList.GetHdlCount();
148
0
        const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : nullptr);
149
150
0
        for(size_t a = 0; a < nHdlCount; ++a)
151
0
        {
152
0
            SdrHdl* pTestHdl = rHdlList.GetHdl(a);
153
154
0
            if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
155
0
            {
156
0
                maHandles.push_back(pTestHdl);
157
0
            }
158
0
        }
159
160
0
        maMove = maOrig;
161
0
        bValid = true;
162
0
    }
163
0
    else
164
0
    {
165
0
        sal_uInt16 nPntMax = 0; // maximum index
166
0
        bValid=false;
167
0
        bClosed=rPO.IsClosed();          // closed object?
168
0
        nPoly=static_cast<sal_uInt16>(rHdl.GetPolyNum());            // number of the polygon in the PolyPolygon
169
0
        nPnt=static_cast<sal_uInt16>(rHdl.GetPointNum());            // number of points in the above polygon
170
0
        const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
171
0
        nPointCount=aTmpXP.GetPointCount();        // number of point of the polygon
172
0
        if (nPointCount==0 || (bClosed && nPointCount==1)) return; // minimum of 1 points for Lines, minimum of 2 points for Polygon
173
0
        nPntMax=nPointCount-1;                  // maximum index
174
0
        bBegPnt=!bClosed && nPnt==0;        // dragged point is first point of a Polyline
175
0
        bEndPnt=!bClosed && nPnt==nPntMax;  // dragged point is finishing point of a Polyline
176
0
        if (bClosed && nPointCount<=3) {        // if polygon is only a line
177
0
            bBegPnt=(nPointCount<3) || nPnt==0;
178
0
            bEndPnt=(nPointCount<3) || nPnt==nPntMax-1;
179
0
        }
180
0
        nPrevPnt=nPnt;                      // index of previous point
181
0
        nNextPnt=nPnt;                      // index of next point
182
0
        if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed);
183
0
        if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
184
0
        bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
185
0
        bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
186
0
        nPrevPrevPnt=nPnt;                  // index of point before previous point
187
0
        nNextNextPnt=nPnt;                  // index of point after next point
188
0
        if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed);
189
0
        if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
190
0
        bControl=rHdl.IsPlusHdl();          // point is a control point
191
0
        bIsNextControl=false;               // point is a control point after a support point
192
0
        bPrevIsControl=false;               // if nPnt is a support point: a control point comes before
193
0
        bNextIsControl=false;               // if nPnt is a support point: a control point comes after
194
0
        if (bControl) {
195
0
            bIsNextControl=!aTmpXP.IsControl(nPrevPnt);
196
0
        } else {
197
0
            bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==PolyFlags::Control;
198
0
            bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==PolyFlags::Control;
199
0
        }
200
0
        nPrevPrevPnt0=nPrevPrevPnt;
201
0
        nPrevPnt0    =nPrevPnt;
202
0
        nPnt0        =nPnt;
203
0
        nNextPnt0    =nNextPnt;
204
0
        nNextNextPnt0=nNextNextPnt;
205
0
        nPrevPrevPnt=0;
206
0
        nPrevPnt=1;
207
0
        nPnt=2;
208
0
        nNextPnt=3;
209
0
        nNextNextPnt=4;
210
0
        bEliminate=false;
211
0
        ResetPoly(rPO);
212
0
        bValid=true;
213
0
    }
214
0
}
215
216
void ImpSdrPathDragData::ResetPoly(const SdrPathObj& rPO)
217
0
{
218
0
    const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
219
0
    aXP[0]=aTmpXP[nPrevPrevPnt0];  aXP.SetFlags(0,aTmpXP.GetFlags(nPrevPrevPnt0));
220
0
    aXP[1]=aTmpXP[nPrevPnt0];      aXP.SetFlags(1,aTmpXP.GetFlags(nPrevPnt0));
221
0
    aXP[2]=aTmpXP[nPnt0];          aXP.SetFlags(2,aTmpXP.GetFlags(nPnt0));
222
0
    aXP[3]=aTmpXP[nNextPnt0];      aXP.SetFlags(3,aTmpXP.GetFlags(nNextPnt0));
223
0
    aXP[4]=aTmpXP[nNextNextPnt0];  aXP.SetFlags(4,aTmpXP.GetFlags(nNextNextPnt0));
224
0
}
225
226
namespace {
227
228
struct ImpPathCreateUser  : public SdrDragStatUserData
229
{
230
    Point                   aBezControl0;
231
    Point                   aBezStart;
232
    Point                   aBezCtrl1;
233
    Point                   aBezCtrl2;
234
    Point                   aBezEnd;
235
    Point                   aCircStart;
236
    Point                   aCircEnd;
237
    Point                   aCircCenter;
238
    Point                   aLineStart;
239
    Point                   aLineEnd;
240
    Point                   aRectP1;
241
    Point                   aRectP2;
242
    Point                   aRectP3;
243
    tools::Long                    nCircRadius;
244
    Degree100               nCircStAngle;
245
    Degree100               nCircRelAngle;
246
    bool                    bBezier;
247
    bool                    bBezHasCtrl0;
248
    bool                    bCircle;
249
    bool                    bAngleSnap;
250
    bool                    bLine;
251
    bool                    bLine90;
252
    bool                    bRect;
253
    bool                    bMixedCreate;
254
    sal_uInt16                  nBezierStartPoint;
255
    SdrObjKind              eStartKind;
256
    SdrObjKind              eCurrentKind;
257
258
public:
259
0
    ImpPathCreateUser(): nCircRadius(0),nCircStAngle(0),nCircRelAngle(0),
260
0
        bBezier(false),bBezHasCtrl0(false),bCircle(false),bAngleSnap(false),bLine(false),bLine90(false),bRect(false),
261
0
        bMixedCreate(false),nBezierStartPoint(0),eStartKind(SdrObjKind::NONE),eCurrentKind(SdrObjKind::NONE) { }
262
263
0
    void ResetFormFlags() { bBezier=false; bCircle=false; bLine=false; bRect=false; }
264
0
    bool IsFormFlag() const { return bBezier || bCircle || bLine || bRect; }
265
    XPolygon GetFormPoly() const;
266
    void CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown);
267
    XPolygon GetBezierPoly() const;
268
    void CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
269
    XPolygon GetCirclePoly() const;
270
    void CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
271
    static Point    CalcLine(const Point& rCsr, tools::Long nDirX, tools::Long nDirY, SdrView const * pView);
272
    XPolygon GetLinePoly() const;
273
    void CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
274
    XPolygon GetRectPoly() const;
275
};
276
277
}
278
279
XPolygon ImpPathCreateUser::GetFormPoly() const
280
0
{
281
0
    if (bBezier) return GetBezierPoly();
282
0
    if (bCircle) return GetCirclePoly();
283
0
    if (bLine)   return GetLinePoly();
284
0
    if (bRect)   return GetRectPoly();
285
0
    return XPolygon();
286
0
}
287
288
void ImpPathCreateUser::CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown)
289
0
{
290
0
    aBezStart=rP1;
291
0
    aBezCtrl1=rP1+rDir;
292
0
    aBezCtrl2=rP2;
293
294
    // #i21479#
295
    // Also copy the end point when no end point is set yet
296
0
    if (!bMouseDown || (0 == aBezEnd.X() && 0 == aBezEnd.Y())) aBezEnd=rP2;
297
298
0
    bBezier=true;
299
0
}
300
301
XPolygon ImpPathCreateUser::GetBezierPoly() const
302
0
{
303
0
    XPolygon aXP(4);
304
0
    aXP[0]=aBezStart; aXP.SetFlags(0,PolyFlags::Smooth);
305
0
    aXP[1]=aBezCtrl1; aXP.SetFlags(1,PolyFlags::Control);
306
0
    aXP[2]=aBezCtrl2; aXP.SetFlags(2,PolyFlags::Control);
307
0
    aXP[3]=aBezEnd;
308
0
    return aXP;
309
0
}
310
311
void ImpPathCreateUser::CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
312
0
{
313
0
    Degree100 nTangAngle=GetAngle(rDir);
314
0
    aCircStart=rP1;
315
0
    aCircEnd=rP2;
316
0
    aCircCenter=rP1;
317
0
    tools::Long dx=rP2.X()-rP1.X();
318
0
    tools::Long dy=rP2.Y()-rP1.Y();
319
0
    Degree100 dAngle=GetAngle(Point(dx,dy))-nTangAngle;
320
0
    dAngle=NormAngle36000(dAngle);
321
0
    Degree100 nTmpAngle=NormAngle36000(9000_deg100-dAngle);
322
0
    bool bRet=nTmpAngle!=9000_deg100 && nTmpAngle!=27000_deg100;
323
0
    tools::Long nRad=0;
324
0
    if (bRet) {
325
0
        double cs = cos(toRadians(nTmpAngle));
326
0
        double nR=static_cast<double>(GetLen(Point(dx,dy)))/cs/2;
327
0
        nRad = std::abs(basegfx::fround<tools::Long>(nR));
328
0
    }
329
0
    if (dAngle<18000_deg100) {
330
0
        nCircStAngle=NormAngle36000(nTangAngle-9000_deg100);
331
0
        nCircRelAngle=NormAngle36000(2_deg100*dAngle);
332
0
        aCircCenter.AdjustX(basegfx::fround<tools::Long>(nRad * cos(toRadians(nTangAngle + 9000_deg100))));
333
0
        aCircCenter.AdjustY(basegfx::fround<tools::Long>(nRad * -sin(toRadians(nTangAngle + 9000_deg100))));
334
0
    } else {
335
0
        nCircStAngle=NormAngle36000(nTangAngle+9000_deg100);
336
0
        nCircRelAngle=-NormAngle36000(36000_deg100-2_deg100*dAngle);
337
0
        aCircCenter.AdjustX(basegfx::fround<tools::Long>(nRad * cos(toRadians(nTangAngle - 9000_deg100))));
338
0
        aCircCenter.AdjustY(basegfx::fround<tools::Long>(nRad * -sin(toRadians(nTangAngle - 9000_deg100))));
339
0
    }
340
0
    bAngleSnap=pView!=nullptr && pView->IsAngleSnapEnabled();
341
0
    if (bAngleSnap) {
342
0
        Degree100 nSA=pView->GetSnapAngle();
343
0
        if (nSA) { // angle snapping
344
0
            bool bNeg=nCircRelAngle<0_deg100;
345
0
            if (bNeg) nCircRelAngle=-nCircRelAngle;
346
0
            nCircRelAngle+=nSA/2_deg100;
347
0
            nCircRelAngle/=nSA;
348
0
            nCircRelAngle*=nSA;
349
0
            nCircRelAngle=NormAngle36000(nCircRelAngle);
350
0
            if (bNeg) nCircRelAngle=-nCircRelAngle;
351
0
        }
352
0
    }
353
0
    nCircRadius=nRad;
354
0
    if (nRad==0 || abs(nCircRelAngle).get()<5) bRet=false;
355
0
    bCircle=bRet;
356
0
}
357
358
XPolygon ImpPathCreateUser::GetCirclePoly() const
359
0
{
360
0
    if (nCircRelAngle>=0_deg100) {
361
0
        XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
362
0
                     nCircStAngle, nCircStAngle+nCircRelAngle,false);
363
0
        aXP[0]=aCircStart; aXP.SetFlags(0,PolyFlags::Smooth);
364
0
        if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
365
0
        return aXP;
366
0
    } else {
367
0
        XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
368
0
                     NormAngle36000(nCircStAngle+nCircRelAngle), nCircStAngle,false);
369
0
        sal_uInt16 nCount=aXP.GetPointCount();
370
0
        for (sal_uInt16 nNum=nCount/2; nNum>0;) {
371
0
            nNum--; // reverse XPoly's order of points
372
0
            sal_uInt16 n2=nCount-nNum-1;
373
0
            Point aPt(aXP[nNum]);
374
0
            aXP[nNum]=aXP[n2];
375
0
            aXP[n2]=aPt;
376
0
        }
377
0
        aXP[0]=aCircStart; aXP.SetFlags(0,PolyFlags::Smooth);
378
0
        if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
379
0
        return aXP;
380
0
    }
381
0
}
382
383
Point ImpPathCreateUser::CalcLine(const Point& aCsr, tools::Long nDirX, tools::Long nDirY, SdrView const * pView)
384
0
{
385
0
    tools::Long x=aCsr.X();
386
0
    tools::Long y=aCsr.Y();
387
0
    bool bHLin=nDirY==0;
388
0
    bool bVLin=nDirX==0;
389
0
    if (bHLin) y=0;
390
0
    else if (bVLin) x=0;
391
0
    else {
392
0
        tools::Long x2=BigMulDiv(y,nDirX,nDirY);
393
0
        tools::Long y2=BigMulDiv(x,nDirY,nDirX);
394
0
        tools::Long l1=std::abs(x2)+std::abs(y);
395
0
        tools::Long l2=std::abs(x)+std::abs(y2);
396
0
        if ((l1<=l2) != (pView!=nullptr && pView->IsBigOrtho())) {
397
0
            x = x2;
398
0
        } else {
399
0
            y = y2;
400
0
        }
401
0
    }
402
0
    return Point(x,y);
403
0
}
404
405
void ImpPathCreateUser::CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
406
0
{
407
0
    aLineStart=rP1;
408
0
    aLineEnd=rP2;
409
0
    bLine90=false;
410
0
    if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bLine=false; return; }
411
0
    Point aTmpPt(rP2-rP1);
412
0
    tools::Long nDirX=rDir.X();
413
0
    tools::Long nDirY=rDir.Y();
414
0
    Point aP1(CalcLine(aTmpPt, nDirX, nDirY,pView)); aP1-=aTmpPt; tools::Long nQ1=std::abs(aP1.X())+std::abs(aP1.Y());
415
0
    Point aP2(CalcLine(aTmpPt, nDirY,-nDirX,pView)); aP2-=aTmpPt; tools::Long nQ2=std::abs(aP2.X())+std::abs(aP2.Y());
416
0
    if (pView!=nullptr && pView->IsOrtho()) nQ1=0; // Ortho turns off at right angle
417
0
    bLine90=nQ1>2*nQ2;
418
0
    if (!bLine90) { // smooth transition
419
0
        aLineEnd+=aP1;
420
0
    } else {          // rectangular transition
421
0
        aLineEnd+=aP2;
422
0
    }
423
0
    bLine=true;
424
0
}
425
426
XPolygon ImpPathCreateUser::GetLinePoly() const
427
0
{
428
0
    XPolygon aXP(2);
429
0
    aXP[0]=aLineStart; if (!bLine90) aXP.SetFlags(0,PolyFlags::Smooth);
430
0
    aXP[1]=aLineEnd;
431
0
    return aXP;
432
0
}
433
434
void ImpPathCreateUser::CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
435
0
{
436
0
    aRectP1=rP1;
437
0
    aRectP2=rP1;
438
0
    aRectP3=rP2;
439
0
    if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bRect=false; return; }
440
0
    Point aTmpPt(rP2-rP1);
441
0
    tools::Long nDirX=rDir.X();
442
0
    tools::Long nDirY=rDir.Y();
443
0
    tools::Long x=aTmpPt.X();
444
0
    tools::Long y=aTmpPt.Y();
445
0
    bool bHLin=nDirY==0;
446
0
    bool bVLin=nDirX==0;
447
0
    if (bHLin) y=0;
448
0
    else if (bVLin) x=0;
449
0
    else {
450
0
        y=BigMulDiv(x,nDirY,nDirX);
451
0
        tools::Long nHypLen=aTmpPt.Y()-y;
452
0
        Degree100 nTangAngle=-GetAngle(rDir);
453
        // sin=g/h, g=h*sin
454
0
        double a = toRadians(nTangAngle);
455
0
        double sn=sin(a);
456
0
        double cs=cos(a);
457
0
        double nGKathLen=nHypLen*sn;
458
0
        y += basegfx::fround<tools::Long>(nGKathLen * sn);
459
0
        x += basegfx::fround<tools::Long>(nGKathLen * cs);
460
0
    }
461
0
    aRectP2.AdjustX(x );
462
0
    aRectP2.AdjustY(y );
463
0
    if (pView!=nullptr && pView->IsOrtho()) {
464
0
        tools::Long dx1=aRectP2.X()-aRectP1.X(); tools::Long dx1a=std::abs(dx1);
465
0
        tools::Long dy1=aRectP2.Y()-aRectP1.Y(); tools::Long dy1a=std::abs(dy1);
466
0
        tools::Long dx2=aRectP3.X()-aRectP2.X(); tools::Long dx2a=std::abs(dx2);
467
0
        tools::Long dy2=aRectP3.Y()-aRectP2.Y(); tools::Long dy2a=std::abs(dy2);
468
0
        bool b1MoreThan2=dx1a+dy1a>dx2a+dy2a;
469
0
        if (b1MoreThan2 != pView->IsBigOrtho()) {
470
0
            tools::Long xtemp=dy2a-dx1a; if (dx1<0) xtemp=-xtemp;
471
0
            tools::Long ytemp=dx2a-dy1a; if (dy1<0) ytemp=-ytemp;
472
0
            aRectP2.AdjustX(xtemp );
473
0
            aRectP2.AdjustY(ytemp );
474
0
            aRectP3.AdjustX(xtemp );
475
0
            aRectP3.AdjustY(ytemp );
476
0
        } else {
477
0
            tools::Long xtemp=dy1a-dx2a; if (dx2<0) xtemp=-xtemp;
478
0
            tools::Long ytemp=dx1a-dy2a; if (dy2<0) ytemp=-ytemp;
479
0
            aRectP3.AdjustX(xtemp );
480
0
            aRectP3.AdjustY(ytemp );
481
0
        }
482
0
    }
483
0
    bRect=true;
484
0
}
485
486
XPolygon ImpPathCreateUser::GetRectPoly() const
487
0
{
488
0
    XPolygon aXP(3);
489
0
    aXP[0]=aRectP1; aXP.SetFlags(0,PolyFlags::Smooth);
490
0
    aXP[1]=aRectP2;
491
0
    if (aRectP3!=aRectP2) aXP[2]=aRectP3;
492
0
    return aXP;
493
0
}
494
495
class ImpPathForDragAndCreate
496
{
497
    SdrPathObj&                 mrSdrPathObject;
498
    XPolyPolygon                aPathPolygon;
499
    SdrObjKind                  meObjectKind;
500
    std::unique_ptr<ImpSdrPathDragData>
501
                                mpSdrPathDragData;
502
    bool                        mbCreating;
503
504
public:
505
    explicit ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject);
506
507
    // drag stuff
508
    bool beginPathDrag( SdrDragStat const & rDrag )  const;
509
    bool movePathDrag( SdrDragStat& rDrag ) const;
510
    bool endPathDrag( SdrDragStat const & rDrag );
511
    OUString getSpecialDragComment(const SdrDragStat& rDrag) const;
512
    basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat& rDrag) const;
513
514
    // create stuff
515
    void BegCreate(SdrDragStat& rStat);
516
    bool MovCreate(SdrDragStat& rStat);
517
    bool EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd);
518
    bool BckCreate(SdrDragStat const & rStat);
519
    void BrkCreate(SdrDragStat& rStat);
520
    PointerStyle GetCreatePointer() const;
521
522
    // helping stuff
523
0
    static bool IsClosed(SdrObjKind eKind) { return eKind==SdrObjKind::Polygon || eKind==SdrObjKind::PathPoly || eKind==SdrObjKind::PathFill || eKind==SdrObjKind::FreehandFill; }
524
0
    static bool IsFreeHand(SdrObjKind eKind) { return eKind==SdrObjKind::FreehandLine || eKind==SdrObjKind::FreehandFill; }
525
0
    static bool IsBezier(SdrObjKind eKind) { return eKind==SdrObjKind::PathLine || eKind==SdrObjKind::PathFill; }
526
0
    bool IsCreating() const { return mbCreating; }
527
528
    // get the polygon
529
    basegfx::B2DPolyPolygon TakeObjectPolyPolygon(const SdrDragStat& rDrag) const;
530
    static basegfx::B2DPolyPolygon TakeDragPolyPolygon(const SdrDragStat& rDrag);
531
0
    basegfx::B2DPolyPolygon getModifiedPolyPolygon() const { return  aPathPolygon.getB2DPolyPolygon(); }
532
};
533
534
ImpPathForDragAndCreate::ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject)
535
0
:   mrSdrPathObject(rSdrPathObject),
536
0
    aPathPolygon(rSdrPathObject.GetPathPoly()),
537
0
    meObjectKind(mrSdrPathObject.meKind),
538
0
    mbCreating(false)
539
0
{
540
0
}
541
542
bool ImpPathForDragAndCreate::beginPathDrag( SdrDragStat const & rDrag )  const
543
0
{
544
0
    const SdrHdl* pHdl=rDrag.GetHdl();
545
0
    if(!pHdl)
546
0
        return false;
547
548
0
    bool bMultiPointDrag(true);
549
550
0
    if(aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())].IsControl(static_cast<sal_uInt16>(pHdl->GetPointNum())))
551
0
        bMultiPointDrag = false;
552
553
0
    if(bMultiPointDrag)
554
0
    {
555
0
        const SdrMarkView& rMarkView = *rDrag.GetView();
556
0
        const SdrHdlList& rHdlList = rMarkView.GetHdlList();
557
0
        const size_t nHdlCount = rHdlList.GetHdlCount();
558
0
        const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : nullptr);
559
0
        sal_uInt32 nSelectedPoints(0);
560
561
0
        for(size_t a = 0; a < nHdlCount; ++a)
562
0
        {
563
0
            SdrHdl* pTestHdl = rHdlList.GetHdl(a);
564
565
0
            if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
566
0
            {
567
0
                nSelectedPoints++;
568
0
            }
569
0
        }
570
571
0
        if(nSelectedPoints <= 1)
572
0
            bMultiPointDrag = false;
573
0
    }
574
575
0
    const_cast<ImpPathForDragAndCreate*>(this)->mpSdrPathDragData.reset( new ImpSdrPathDragData(mrSdrPathObject,*pHdl,bMultiPointDrag,rDrag) );
576
577
0
    if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
578
0
    {
579
0
        OSL_FAIL("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData is invalid.");
580
0
        const_cast<ImpPathForDragAndCreate*>(this)->mpSdrPathDragData.reset();
581
0
        return false;
582
0
    }
583
584
0
    return true;
585
0
}
586
587
bool ImpPathForDragAndCreate::movePathDrag( SdrDragStat& rDrag ) const
588
0
{
589
0
    if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
590
0
    {
591
0
        OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
592
0
        return false;
593
0
    }
594
595
0
    if(mpSdrPathDragData->IsMultiPointDrag())
596
0
    {
597
0
        Point aDelta(rDrag.GetNow() - rDrag.GetStart());
598
599
0
        if(aDelta.X() || aDelta.Y())
600
0
        {
601
0
            for(SdrHdl* pHandle : mpSdrPathDragData->maHandles)
602
0
            {
603
0
                const sal_uInt16 nPolyIndex(static_cast<sal_uInt16>(pHandle->GetPolyNum()));
604
0
                const sal_uInt16 nPointIndex(static_cast<sal_uInt16>(pHandle->GetPointNum()));
605
0
                const XPolygon& rOrig = mpSdrPathDragData->maOrig[nPolyIndex];
606
0
                XPolygon& rMove = mpSdrPathDragData->maMove[nPolyIndex];
607
0
                const sal_uInt16 nPointCount(rOrig.GetPointCount());
608
0
                bool bClosed(rOrig[0] == rOrig[nPointCount-1]);
609
610
                // move point itself
611
0
                rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
612
613
                // when point is first and poly closed, move close point, too.
614
0
                if(nPointCount > 0 && !nPointIndex && bClosed)
615
0
                {
616
0
                    rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
617
618
                    // when moving the last point it may be necessary to move the
619
                    // control point in front of this one, too.
620
0
                    if(nPointCount > 1 && rOrig.IsControl(nPointCount - 2))
621
0
                        rMove[nPointCount - 2] = rOrig[nPointCount - 2] + aDelta;
622
0
                }
623
624
                // is a control point before this?
625
0
                if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
626
0
                {
627
                    // Yes, move it, too
628
0
                    rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
629
0
                }
630
631
                // is a control point after this?
632
0
                if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
633
0
                {
634
                    // Yes, move it, too
635
0
                    rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
636
0
                }
637
0
            }
638
0
        }
639
0
    }
640
0
    else
641
0
    {
642
0
        mpSdrPathDragData->ResetPoly(mrSdrPathObject);
643
644
        // copy certain data locally to use less code and have faster access times
645
0
        bool bClosed           =mpSdrPathDragData->bClosed       ; // closed object?
646
0
        sal_uInt16   nPnt          =mpSdrPathDragData->nPnt          ; // number of point in the above polygon
647
0
        bool bBegPnt           =mpSdrPathDragData->bBegPnt       ; // dragged point is first point of a Polyline
648
0
        bool bEndPnt           =mpSdrPathDragData->bEndPnt       ; // dragged point is last point of a Polyline
649
0
        sal_uInt16   nPrevPnt      =mpSdrPathDragData->nPrevPnt      ; // index of previous point
650
0
        sal_uInt16   nNextPnt      =mpSdrPathDragData->nNextPnt      ; // index of next point
651
0
        bool bPrevIsBegPnt     =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
652
0
        bool bNextIsEndPnt     =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
653
0
        sal_uInt16   nPrevPrevPnt  =mpSdrPathDragData->nPrevPrevPnt  ; // index of the point before the previous point
654
0
        sal_uInt16   nNextNextPnt  =mpSdrPathDragData->nNextNextPnt  ; // index if the point after the next point
655
0
        bool bControl          =mpSdrPathDragData->bControl      ; // point is a control point
656
0
        bool bIsNextControl    =mpSdrPathDragData->bIsNextControl; // point is a control point after a support point
657
0
        bool bPrevIsControl    =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
658
0
        bool bNextIsControl    =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
659
660
        // Ortho for lines/polygons: keep angle
661
0
        if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho()) {
662
0
            bool bBigOrtho=rDrag.GetView()->IsBigOrtho();
663
0
            Point  aPos(rDrag.GetNow());      // current position
664
0
            Point  aPnt(mpSdrPathDragData->aXP[nPnt]);      // the dragged point
665
0
            sal_uInt16 nPnt1=0xFFFF,nPnt2=0xFFFF; // its neighboring points
666
0
            Point  aNewPos1,aNewPos2;         // new alternative for aPos
667
0
            bool bPnt1 = false, bPnt2 = false; // are these valid alternatives?
668
0
            if (!bClosed && mpSdrPathDragData->nPointCount>=2) { // minimum of 2 points for lines
669
0
                if (!bBegPnt) nPnt1=nPrevPnt;
670
0
                if (!bEndPnt) nPnt2=nNextPnt;
671
0
            }
672
0
            if (bClosed && mpSdrPathDragData->nPointCount>=3) { // minimum of 3 points for polygon
673
0
                nPnt1=nPrevPnt;
674
0
                nPnt2=nNextPnt;
675
0
            }
676
0
            if (nPnt1!=0xFFFF && !bPrevIsControl) {
677
0
                Point aPnt1=mpSdrPathDragData->aXP[nPnt1];
678
0
                tools::Long ndx0=aPnt.X()-aPnt1.X();
679
0
                tools::Long ndy0=aPnt.Y()-aPnt1.Y();
680
0
                bool bHLin=ndy0==0;
681
0
                bool bVLin=ndx0==0;
682
0
                if (!bHLin || !bVLin) {
683
0
                    tools::Long ndx=aPos.X()-aPnt1.X();
684
0
                    tools::Long ndy=aPos.Y()-aPnt1.Y();
685
0
                    bPnt1=true;
686
0
                    double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
687
0
                    double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
688
0
                    bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
689
0
                    bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
690
0
                    if (bHor) ndy=tools::Long(ndy0*nXFact);
691
0
                    if (bVer) ndx=tools::Long(ndx0*nYFact);
692
0
                    aNewPos1=aPnt1;
693
0
                    aNewPos1.AdjustX(ndx );
694
0
                    aNewPos1.AdjustY(ndy );
695
0
                }
696
0
            }
697
0
            if (nPnt2!=0xFFFF && !bNextIsControl) {
698
0
                Point aPnt2=mpSdrPathDragData->aXP[nPnt2];
699
0
                tools::Long ndx0=aPnt.X()-aPnt2.X();
700
0
                tools::Long ndy0=aPnt.Y()-aPnt2.Y();
701
0
                bool bHLin=ndy0==0;
702
0
                bool bVLin=ndx0==0;
703
0
                if (!bHLin || !bVLin) {
704
0
                    tools::Long ndx=aPos.X()-aPnt2.X();
705
0
                    tools::Long ndy=aPos.Y()-aPnt2.Y();
706
0
                    bPnt2=true;
707
0
                    double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
708
0
                    double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
709
0
                    bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
710
0
                    bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
711
0
                    if (bHor) ndy=tools::Long(ndy0*nXFact);
712
0
                    if (bVer) ndx=tools::Long(ndx0*nYFact);
713
0
                    aNewPos2=aPnt2;
714
0
                    aNewPos2.AdjustX(ndx );
715
0
                    aNewPos2.AdjustY(ndy );
716
0
                }
717
0
            }
718
0
            if (bPnt1 && bPnt2) { // both alternatives exist (and compete)
719
0
                BigInt nX1(aNewPos1.X()-aPos.X()); nX1*=nX1;
720
0
                BigInt nY1(aNewPos1.Y()-aPos.Y()); nY1*=nY1;
721
0
                BigInt nX2(aNewPos2.X()-aPos.X()); nX2*=nX2;
722
0
                BigInt nY2(aNewPos2.Y()-aPos.Y()); nY2*=nY2;
723
0
                nX1+=nY1; // correction distance to square
724
0
                nX2+=nY2; // correction distance to square
725
                // let the alternative that allows fewer correction win
726
0
                if (nX1<nX2) bPnt2=false; else bPnt1=false;
727
0
            }
728
0
            if (bPnt1) rDrag.SetNow(aNewPos1);
729
0
            if (bPnt2) rDrag.SetNow(aNewPos2);
730
0
        }
731
0
        rDrag.SetActionRect(tools::Rectangle(rDrag.GetNow(),rDrag.GetNow()));
732
733
        // specially for IBM: Eliminate points if both adjoining lines form near 180 degrees angle anyway
734
0
        if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsEliminatePolyPoints() &&
735
0
            !bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
736
0
        {
737
0
            Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
738
0
            aPt-=rDrag.GetNow();
739
0
            Degree100 nAngle1=GetAngle(aPt);
740
0
            aPt=rDrag.GetNow();
741
0
            aPt-=mpSdrPathDragData->aXP[nPrevPnt];
742
0
            Degree100 nAngle2=GetAngle(aPt);
743
0
            Degree100 nDiff=nAngle1-nAngle2;
744
0
            nDiff=abs(nDiff);
745
0
            mpSdrPathDragData->bEliminate=nDiff<=rDrag.GetView()->GetEliminatePolyPointLimitAngle();
746
0
            if (mpSdrPathDragData->bEliminate) { // adapt position, Smooth is true for the ends
747
0
                aPt=mpSdrPathDragData->aXP[nNextPnt];
748
0
                aPt+=mpSdrPathDragData->aXP[nPrevPnt];
749
0
                aPt/=2;
750
0
                rDrag.SetNow(aPt);
751
0
            }
752
0
        }
753
754
        // we dragged by this distance
755
0
        Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
756
757
        /* There are 8 possible cases:
758
              X      1. A control point neither on the left nor on the right.
759
           o--X--o   2. There are control points on the left and the right, we are dragging a support point.
760
           o--X      3. There is a control point on the left, we are dragging a support point.
761
              X--o   4. There is a control point on the right, we are dragging a support point.
762
           x--O--o   5. There are control points on the left and the right, we are dragging the left one.
763
           x--O      6. There is a control point on the left, we are dragging it.
764
           o--O--x   7. There are control points on the left and the right, we are dragging the right one.
765
              O--x   8. There is a control point on the right, we are dragging it.
766
           Note: modifying a line (not a curve!) might create a curve on the other end of the line
767
           if Smooth is set there (with control points aligned to line).
768
        */
769
770
0
        mpSdrPathDragData->aXP[nPnt]+=aDiff;
771
772
        // now check symmetric plus handles
773
0
        if (bControl) { // cases 5,6,7,8
774
0
            sal_uInt16   nSt; // the associated support point
775
0
            sal_uInt16   nFix;  // the opposing control point
776
0
            if (bIsNextControl) { // if the next one is a control point, the on before has to be a support point
777
0
                nSt=nPrevPnt;
778
0
                nFix=nPrevPrevPnt;
779
0
            } else {
780
0
                nSt=nNextPnt;
781
0
                nFix=nNextNextPnt;
782
0
            }
783
0
            if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
784
0
                mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
785
0
            }
786
0
        }
787
788
0
        if (!bControl) { // Cases 1,2,3,4. In case 1, nothing happens; in cases 3 and 4, there is more following below.
789
            // move both control points
790
0
            if (bPrevIsControl) mpSdrPathDragData->aXP[nPrevPnt]+=aDiff;
791
0
            if (bNextIsControl) mpSdrPathDragData->aXP[nNextPnt]+=aDiff;
792
            // align control point to line, if appropriate
793
0
            if (mpSdrPathDragData->aXP.IsSmooth(nPnt)) {
794
0
                if (bPrevIsControl && !bNextIsControl && !bEndPnt) { // case 3
795
0
                    mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nNextPnt,nPrevPnt);
796
0
                }
797
0
                if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // case 4
798
0
                    mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
799
0
                }
800
0
            }
801
            // Now check the other ends of the line (nPnt+-1). If there is a
802
            // curve (IsControl(nPnt+-2)) with SmoothJoin (nPnt+-1), the
803
            // associated control point (nPnt+-2) has to be adapted.
804
0
            if (!bBegPnt && !bPrevIsControl && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsSmooth(nPrevPnt)) {
805
0
                if (mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
806
0
                    mpSdrPathDragData->aXP.CalcSmoothJoin(nPrevPnt,nPnt,nPrevPrevPnt);
807
0
                }
808
0
            }
809
0
            if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) {
810
0
                if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
811
0
                    mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
812
0
                }
813
0
            }
814
0
        }
815
0
    }
816
817
0
    return true;
818
0
}
819
820
bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat const & rDrag)
821
0
{
822
0
    Point aLinePt1;
823
0
    Point aLinePt2;
824
0
    bool bLineGlueMirror(SdrObjKind::Line == meObjectKind);
825
0
    if (bLineGlueMirror) {
826
0
        XPolygon& rXP=aPathPolygon[0];
827
0
        aLinePt1=rXP[0];
828
0
        aLinePt2=rXP[1];
829
0
    }
830
831
0
    if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
832
0
    {
833
0
        OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
834
0
        return false;
835
0
    }
836
837
0
    if(mpSdrPathDragData->IsMultiPointDrag())
838
0
    {
839
0
        aPathPolygon = mpSdrPathDragData->maMove;
840
0
    }
841
0
    else
842
0
    {
843
0
        const SdrHdl* pHdl=rDrag.GetHdl();
844
845
        // reference the polygon
846
0
        XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())];
847
848
        // the 5 points that might have changed
849
0
        if (!mpSdrPathDragData->bPrevIsBegPnt) rXP[mpSdrPathDragData->nPrevPrevPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPrevPnt];
850
0
        if (!mpSdrPathDragData->bNextIsEndPnt) rXP[mpSdrPathDragData->nNextNextPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nNextNextPnt];
851
0
        if (!mpSdrPathDragData->bBegPnt)       rXP[mpSdrPathDragData->nPrevPnt0]    =mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPnt];
852
0
        if (!mpSdrPathDragData->bEndPnt)       rXP[mpSdrPathDragData->nNextPnt0]    =mpSdrPathDragData->aXP[mpSdrPathDragData->nNextPnt];
853
0
        rXP[mpSdrPathDragData->nPnt0]        =mpSdrPathDragData->aXP[mpSdrPathDragData->nPnt];
854
855
        // for closed objects: last point has to be equal to first point
856
0
        if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
857
858
0
        if (mpSdrPathDragData->bEliminate)
859
0
        {
860
0
            basegfx::B2DPolyPolygon aTempPolyPolygon(aPathPolygon.getB2DPolyPolygon());
861
0
            sal_uInt32 nPoly,nPnt;
862
863
0
            if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon, rDrag.GetHdl()->GetSourceHdlNum(), nPoly, nPnt))
864
0
            {
865
0
                basegfx::B2DPolygon aCandidate(aTempPolyPolygon.getB2DPolygon(nPoly));
866
0
                aCandidate.remove(nPnt);
867
868
0
                if(aCandidate.count() < 2)
869
0
                {
870
0
                    aTempPolyPolygon.remove(nPoly);
871
0
                }
872
0
                else
873
0
                {
874
0
                    aTempPolyPolygon.setB2DPolygon(nPoly, aCandidate);
875
0
                }
876
0
            }
877
878
0
            aPathPolygon = XPolyPolygon(aTempPolyPolygon);
879
0
        }
880
881
        // adapt angle for text beneath a simple line
882
0
        if (bLineGlueMirror)
883
0
        {
884
0
            Point aLinePt1_(aPathPolygon[0][0]);
885
0
            Point aLinePt2_(aPathPolygon[0][1]);
886
0
            bool bXMirr=(aLinePt1_.X()>aLinePt2_.X())!=(aLinePt1.X()>aLinePt2.X());
887
0
            bool bYMirr=(aLinePt1_.Y()>aLinePt2_.Y())!=(aLinePt1.Y()>aLinePt2.Y());
888
0
            if (bXMirr || bYMirr) {
889
0
                Point aRef1(mrSdrPathObject.GetSnapRect().Center());
890
0
                if (bXMirr) {
891
0
                    Point aRef2(aRef1);
892
0
                    aRef2.AdjustY( 1 );
893
0
                    mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
894
0
                }
895
0
                if (bYMirr) {
896
0
                    Point aRef2(aRef1);
897
0
                    aRef2.AdjustX( 1 );
898
0
                    mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
899
0
                }
900
0
            }
901
0
        }
902
0
    }
903
904
0
    mpSdrPathDragData.reset();
905
906
0
    return true;
907
0
}
908
909
OUString ImpPathForDragAndCreate::getSpecialDragComment(const SdrDragStat& rDrag) const
910
0
{
911
0
    OUString aStr;
912
0
    const SdrHdl* pHdl = rDrag.GetHdl();
913
0
    const bool bCreateComment(rDrag.GetView() && &mrSdrPathObject == rDrag.GetView()->GetCreateObj());
914
915
0
    if(bCreateComment && rDrag.GetUser())
916
0
    {
917
        // #i103058# re-add old creation comment mode
918
0
        const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
919
0
        const SdrObjKind eOriginalKind(meObjectKind);
920
0
        mrSdrPathObject.meKind = pU->eCurrentKind;
921
0
        aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewCreateObj);
922
0
        mrSdrPathObject.meKind = eOriginalKind;
923
924
0
        Point aPrev(rDrag.GetPrev());
925
0
        Point aNow(rDrag.GetNow());
926
927
0
        if(pU->bLine)
928
0
            aNow = pU->aLineEnd;
929
930
0
        aNow -= aPrev;
931
0
        aStr += " (";
932
933
0
        if(pU->bCircle)
934
0
        {
935
0
            aStr += SdrModel::GetAngleString(abs(pU->nCircRelAngle))
936
0
                    + " r="
937
0
                    + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(pU->nCircRadius, true);
938
0
        }
939
940
0
        aStr += "dx="
941
0
                + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.X(), true)
942
0
                + " dy="
943
0
                + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.Y(), true);
944
945
0
        if(!IsFreeHand(meObjectKind))
946
0
        {
947
0
            sal_Int32 nLen(GetLen(aNow));
948
0
            Degree100 nAngle(GetAngle(aNow));
949
0
            aStr += "  l="
950
0
                    + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
951
0
                    + " "
952
0
                    + SdrModel::GetAngleString(nAngle);
953
0
        }
954
955
0
        aStr += ")";
956
0
    }
957
0
    else if(!pHdl)
958
0
    {
959
        // #i103058# fallback when no model and/or Handle, both needed
960
        // for else-path
961
0
        aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_DragPathObj);
962
0
    }
963
0
    else
964
0
    {
965
        // #i103058# standard for modification; model and handle needed
966
0
        ImpSdrPathDragData* pDragData = mpSdrPathDragData.get();
967
968
0
        if(!pDragData)
969
0
        {
970
            // getSpecialDragComment is also used from create, so fallback to GetUser()
971
            // when mpSdrPathDragData is not set
972
0
            pDragData = static_cast<ImpSdrPathDragData*>(rDrag.GetUser());
973
0
        }
974
975
0
        if(!pDragData)
976
0
        {
977
0
            OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
978
0
            return OUString();
979
0
        }
980
981
0
        if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
982
0
        {
983
            // point of ...
984
0
            aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewMarkedPoint);
985
986
            // delete %O
987
0
            OUString aStr2(SvxResId(STR_EditDelete));
988
989
            // UNICODE: delete point of ...
990
0
            aStr2 = aStr2.replaceFirst("%1", aStr);
991
992
0
            return aStr2;
993
0
        }
994
995
        // dx=0.00 dy=0.00                -- both sides bezier
996
        // dx=0.00 dy=0.00  l=0.00 0.00\302\260  -- one bezier/lever on one side, a start, or an ending
997
        // dx=0.00 dy=0.00  l=0.00 0.00\302\260 / l=0.00 0.00\302\260 -- in between
998
0
        Point aBeg(rDrag.GetStart());
999
0
        Point aNow(rDrag.GetNow());
1000
1001
0
        aStr.clear();
1002
0
        aStr += "dx="
1003
0
                + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.X() - aBeg.X(), true)
1004
0
                + " dy="
1005
0
                + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.Y() - aBeg.Y(), true);
1006
1007
0
        if(!pDragData->IsMultiPointDrag())
1008
0
        {
1009
0
            sal_uInt16 nPntNum(static_cast<sal_uInt16>(pHdl->GetPointNum()));
1010
0
            const XPolygon& rXPoly = aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())];
1011
0
            sal_uInt16 nPointCount(rXPoly.GetPointCount());
1012
0
            bool bClose(IsClosed(meObjectKind));
1013
1014
0
            if(bClose)
1015
0
                nPointCount--;
1016
1017
0
            if(pHdl->IsPlusHdl())
1018
0
            {
1019
                // lever
1020
0
                sal_uInt16 nRef(nPntNum);
1021
1022
0
                if(rXPoly.IsControl(nPntNum + 1))
1023
0
                    nRef--;
1024
0
                else
1025
0
                    nRef++;
1026
1027
0
                aNow -= rXPoly[nRef];
1028
1029
0
                sal_Int32 nLen(GetLen(aNow));
1030
0
                Degree100 nAngle(GetAngle(aNow));
1031
0
                aStr += "  l="
1032
0
                        + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
1033
0
                        + " "
1034
0
                        + SdrModel::GetAngleString(nAngle);
1035
0
            }
1036
0
            else if(nPointCount > 1)
1037
0
            {
1038
0
                sal_uInt16 nPntMax(nPointCount - 1);
1039
0
                bool bIsClosed(IsClosed(meObjectKind));
1040
0
                bool bPt1(nPntNum > 0);
1041
0
                bool bPt2(nPntNum < nPntMax);
1042
1043
0
                if(bIsClosed && nPointCount > 2)
1044
0
                {
1045
0
                    bPt1 = true;
1046
0
                    bPt2 = true;
1047
0
                }
1048
1049
0
                sal_uInt16 nPt1,nPt2;
1050
1051
0
                if(nPntNum > 0)
1052
0
                    nPt1 = nPntNum - 1;
1053
0
                else
1054
0
                    nPt1 = nPntMax;
1055
1056
0
                if(nPntNum < nPntMax)
1057
0
                    nPt2 = nPntNum + 1;
1058
0
                else
1059
0
                    nPt2 = 0;
1060
1061
0
                if(bPt1 && rXPoly.IsControl(nPt1))
1062
0
                    bPt1 = false; // don't display
1063
1064
0
                if(bPt2 && rXPoly.IsControl(nPt2))
1065
0
                    bPt2 = false; // of bezier data
1066
1067
0
                if(bPt1)
1068
0
                {
1069
0
                    Point aPt(aNow);
1070
0
                    aPt -= rXPoly[nPt1];
1071
1072
0
                    sal_Int32 nLen(GetLen(aPt));
1073
0
                    Degree100 nAngle(GetAngle(aPt));
1074
0
                    aStr += "  l="
1075
0
                            + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
1076
0
                            + " "
1077
0
                            + SdrModel::GetAngleString(nAngle);
1078
0
                }
1079
1080
0
                if(bPt2)
1081
0
                {
1082
0
                    if(bPt1)
1083
0
                        aStr += " / ";
1084
0
                    else
1085
0
                        aStr += "  ";
1086
1087
0
                    Point aPt(aNow);
1088
0
                    aPt -= rXPoly[nPt2];
1089
1090
0
                    sal_Int32 nLen(GetLen(aPt));
1091
0
                    Degree100 nAngle(GetAngle(aPt));
1092
0
                    aStr += "l="
1093
0
                            + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
1094
0
                            + " "
1095
0
                            + SdrModel::GetAngleString(nAngle);
1096
0
                }
1097
0
            }
1098
0
        }
1099
0
    }
1100
1101
0
    return aStr;
1102
0
}
1103
1104
basegfx::B2DPolyPolygon ImpPathForDragAndCreate::getSpecialDragPoly(const SdrDragStat& rDrag) const
1105
0
{
1106
0
    if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
1107
0
    {
1108
0
        OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
1109
0
        return basegfx::B2DPolyPolygon();
1110
0
    }
1111
1112
0
    XPolyPolygon aRetval;
1113
1114
0
    if(mpSdrPathDragData->IsMultiPointDrag())
1115
0
    {
1116
0
        aRetval.Insert(mpSdrPathDragData->maMove);
1117
0
    }
1118
0
    else
1119
0
    {
1120
0
        const XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())];
1121
0
        if (rXP.GetPointCount()<=2) {
1122
0
            XPolygon aXPoly(rXP);
1123
0
            aXPoly[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPointNum())]=rDrag.GetNow();
1124
0
            aRetval.Insert(std::move(aXPoly));
1125
0
            return aRetval.getB2DPolyPolygon();
1126
0
        }
1127
        // copy certain data locally to use less code and have faster access times
1128
0
        bool bClosed           =mpSdrPathDragData->bClosed       ; // closed object?
1129
0
        sal_uInt16 nPointCount = mpSdrPathDragData->nPointCount; // number of points
1130
0
        sal_uInt16   nPnt          =mpSdrPathDragData->nPnt          ; // number of points in the polygon
1131
0
        bool bBegPnt           =mpSdrPathDragData->bBegPnt       ; // dragged point is the first point of a Polyline
1132
0
        bool bEndPnt           =mpSdrPathDragData->bEndPnt       ; // dragged point is the last point of a Polyline
1133
0
        sal_uInt16   nPrevPnt      =mpSdrPathDragData->nPrevPnt      ; // index of the previous point
1134
0
        sal_uInt16   nNextPnt      =mpSdrPathDragData->nNextPnt      ; // index of the next point
1135
0
        bool bPrevIsBegPnt     =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
1136
0
        bool bNextIsEndPnt     =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
1137
0
        sal_uInt16   nPrevPrevPnt  =mpSdrPathDragData->nPrevPrevPnt  ; // index of the point before the previous point
1138
0
        sal_uInt16   nNextNextPnt  =mpSdrPathDragData->nNextNextPnt  ; // index of the point after the last point
1139
0
        bool bControl          =mpSdrPathDragData->bControl      ; // point is a control point
1140
0
        bool bIsNextControl    =mpSdrPathDragData->bIsNextControl; //point is a control point after a support point
1141
0
        bool bPrevIsControl    =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
1142
0
        bool bNextIsControl    =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
1143
0
        XPolygon aXPoly(mpSdrPathDragData->aXP);
1144
0
        XPolygon aLine1(2);
1145
0
        XPolygon aLine2(2);
1146
0
        XPolygon aLine3(2);
1147
0
        XPolygon aLine4(2);
1148
0
        if (bControl) {
1149
0
            aLine1[1]=mpSdrPathDragData->aXP[nPnt];
1150
0
            if (bIsNextControl) { // is this a control point after the support point?
1151
0
                aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
1152
0
                aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
1153
0
                aLine2[1]=mpSdrPathDragData->aXP[nNextPnt];
1154
0
                if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1155
0
                    aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
1156
0
                    aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
1157
                    // leverage lines for the opposing curve segment
1158
0
                    aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
1159
0
                    aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1160
0
                    aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
1161
0
                    aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
1162
0
                } else {
1163
0
                    aXPoly.Remove(0,1);
1164
0
                }
1165
0
            } else { // else this is a control point before a support point
1166
0
                aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
1167
0
                aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1168
0
                aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt];
1169
0
                if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1170
0
                    aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
1171
0
                    aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
1172
                    // leverage lines for the opposing curve segment
1173
0
                    aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
1174
0
                    aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
1175
0
                    aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
1176
0
                    aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
1177
0
                } else {
1178
0
                    aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1179
0
                }
1180
0
            }
1181
0
        } else { // else is not a control point
1182
0
            if (mpSdrPathDragData->bEliminate) {
1183
0
                aXPoly.Remove(2,1);
1184
0
            }
1185
0
            if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Normal);
1186
0
            else if (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1187
0
                aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
1188
0
                aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
1189
0
            } else {
1190
0
                aXPoly.Remove(0,1);
1191
0
                if (bBegPnt) aXPoly.Remove(0,1);
1192
0
            }
1193
0
            if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Normal);
1194
0
            else if (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1195
0
                aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
1196
0
                aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
1197
0
            } else {
1198
0
                aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1199
0
                if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1200
0
            }
1201
0
            if (bClosed) { // "pear problem": 2 lines, 1 curve, everything smoothed, a point between both lines is dragged
1202
0
                if (aXPoly.GetPointCount()>nPointCount && aXPoly.IsControl(1)) {
1203
0
                    sal_uInt16 a=aXPoly.GetPointCount();
1204
0
                    aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
1205
0
                    aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
1206
0
                    aXPoly.Remove(0,3);
1207
0
                }
1208
0
            }
1209
0
        }
1210
0
        aRetval.Insert(std::move(aXPoly));
1211
0
        if (aLine1.GetPointCount()>1) aRetval.Insert(std::move(aLine1));
1212
0
        if (aLine2.GetPointCount()>1) aRetval.Insert(std::move(aLine2));
1213
0
        if (aLine3.GetPointCount()>1) aRetval.Insert(std::move(aLine3));
1214
0
        if (aLine4.GetPointCount()>1) aRetval.Insert(std::move(aLine4));
1215
0
    }
1216
1217
0
    return aRetval.getB2DPolyPolygon();
1218
0
}
1219
1220
void ImpPathForDragAndCreate::BegCreate(SdrDragStat& rStat)
1221
0
{
1222
0
    bool bFreeHand(IsFreeHand(meObjectKind));
1223
0
    rStat.SetNoSnap(bFreeHand);
1224
0
    rStat.SetOrtho8Possible();
1225
0
    aPathPolygon.Clear();
1226
0
    mbCreating=true;
1227
0
    bool bMakeStartPoint = true;
1228
0
    SdrView* pView=rStat.GetView();
1229
0
    if (pView!=nullptr && pView->IsUseIncompatiblePathCreateInterface() &&
1230
0
        (meObjectKind==SdrObjKind::Polygon || meObjectKind==SdrObjKind::PolyLine || meObjectKind==SdrObjKind::PathLine || meObjectKind==SdrObjKind::PathFill)) {
1231
0
        bMakeStartPoint = false;
1232
0
    }
1233
0
    aPathPolygon.Insert(XPolygon());
1234
0
    aPathPolygon[0][0]=rStat.GetStart();
1235
0
    if (bMakeStartPoint) {
1236
0
        aPathPolygon[0][1]=rStat.GetNow();
1237
0
    }
1238
0
    std::unique_ptr<ImpPathCreateUser> pU(new ImpPathCreateUser);
1239
0
    pU->eStartKind=meObjectKind;
1240
0
    pU->eCurrentKind=meObjectKind;
1241
0
    rStat.SetUser(std::move(pU));
1242
0
}
1243
1244
bool ImpPathForDragAndCreate::MovCreate(SdrDragStat& rStat)
1245
0
{
1246
0
    ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
1247
0
    SdrView* pView=rStat.GetView();
1248
0
    XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1249
0
    if (pView!=nullptr && pView->IsCreateMode()) {
1250
        // switch to different CreateTool, if appropriate
1251
0
        SdrObjKind nIdent;
1252
0
        SdrInventor nInvent;
1253
0
        pView->TakeCurrentObj(nIdent,nInvent);
1254
0
        if (nInvent==SdrInventor::Default && pU->eCurrentKind != nIdent) {
1255
0
            SdrObjKind eNewKind = nIdent;
1256
0
            switch (eNewKind) {
1257
0
                case SdrObjKind::CircleArc:
1258
0
                case SdrObjKind::CircleOrEllipse:
1259
0
                case SdrObjKind::CircleCut:
1260
0
                case SdrObjKind::CircleSection:
1261
0
                    eNewKind=SdrObjKind::CircleArc;
1262
0
                    [[fallthrough]];
1263
0
                case SdrObjKind::Rectangle:
1264
0
                case SdrObjKind::Line:
1265
0
                case SdrObjKind::PolyLine:
1266
0
                case SdrObjKind::Polygon:
1267
0
                case SdrObjKind::PathLine:
1268
0
                case SdrObjKind::PathFill:
1269
0
                case SdrObjKind::FreehandLine:
1270
0
                case SdrObjKind::FreehandFill:
1271
0
                {
1272
0
                    pU->eCurrentKind=eNewKind;
1273
0
                    pU->bMixedCreate=true;
1274
0
                    pU->nBezierStartPoint=rXPoly.GetPointCount();
1275
0
                    if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
1276
0
                } break;
1277
0
                default: break;
1278
0
            } // switch
1279
0
        }
1280
0
    }
1281
0
    sal_uInt16 nCurrentPoint=rXPoly.GetPointCount();
1282
0
    if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nCurrentPoint<2) {
1283
0
        rXPoly[0]=rStat.GetPos0();
1284
0
        rXPoly[1]=rStat.GetNow();
1285
0
        nCurrentPoint=2;
1286
0
    }
1287
0
    if (nCurrentPoint==0) {
1288
0
        rXPoly[0]=rStat.GetPos0();
1289
0
    } else nCurrentPoint--;
1290
0
    bool bFreeHand=IsFreeHand(pU->eCurrentKind);
1291
0
    rStat.SetNoSnap(bFreeHand);
1292
0
    rStat.SetOrtho8Possible(pU->eCurrentKind!=SdrObjKind::CircleArc && pU->eCurrentKind!=SdrObjKind::Rectangle && (!pU->bMixedCreate || pU->eCurrentKind!=SdrObjKind::Line));
1293
0
    rXPoly[nCurrentPoint]=rStat.GetNow();
1294
0
    if (!pU->bMixedCreate && pU->eStartKind==SdrObjKind::Line && rXPoly.GetPointCount()>=1) {
1295
0
        Point aPt(rStat.GetStart());
1296
0
        if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
1297
0
            aPt+=aPt;
1298
0
            aPt-=rStat.GetNow();
1299
0
        }
1300
0
        rXPoly[0]=aPt;
1301
0
    }
1302
0
    OutputDevice* pOut=pView==nullptr ? nullptr : pView->GetFirstOutputDevice();
1303
0
    if (bFreeHand) {
1304
0
        if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint;
1305
0
        if (rStat.IsMouseDown() && nCurrentPoint>0) {
1306
            // don't allow two consecutive points to occupy too similar positions
1307
0
            tools::Long nMinDist=1;
1308
0
            if (pView!=nullptr) nMinDist=pView->GetFreeHandMinDistPix();
1309
0
            if (pOut!=nullptr) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width();
1310
0
            if (nMinDist<1) nMinDist=1;
1311
1312
0
            Point aPt0(rXPoly[nCurrentPoint-1]);
1313
0
            Point aPt1(rStat.GetNow());
1314
0
            tools::Long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
1315
0
            tools::Long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy;
1316
0
            if (dx<nMinDist && dy<nMinDist) return false;
1317
1318
            // TODO: the following is copied from EndCreate (with a few smaller modifications)
1319
            // and should be combined into a method with the code there.
1320
1321
0
            if (nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
1322
0
                rXPoly.PointsToBezier(nCurrentPoint-3);
1323
0
                rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
1324
0
                rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
1325
1326
0
                if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
1327
0
                    rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
1328
0
                    rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
1329
0
                }
1330
0
            }
1331
0
            rXPoly[nCurrentPoint+1]=rStat.GetNow();
1332
0
            rStat.NextPoint();
1333
0
        } else {
1334
0
            pU->nBezierStartPoint=nCurrentPoint;
1335
0
        }
1336
0
    }
1337
1338
0
    pU->ResetFormFlags();
1339
0
    if (IsBezier(pU->eCurrentKind)) {
1340
0
        if (nCurrentPoint>=2) {
1341
0
            pU->CalcBezier(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],rStat.IsMouseDown());
1342
0
        } else if (pU->bBezHasCtrl0) {
1343
0
            pU->CalcBezier(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],pU->aBezControl0-rXPoly[nCurrentPoint-1],rStat.IsMouseDown());
1344
0
        }
1345
0
    }
1346
0
    if (pU->eCurrentKind==SdrObjKind::CircleArc && nCurrentPoint>=2) {
1347
0
        pU->CalcCircle(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
1348
0
    }
1349
0
    if (pU->eCurrentKind==SdrObjKind::Line && nCurrentPoint>=2) {
1350
0
        pU->CalcLine(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
1351
0
    }
1352
0
    if (pU->eCurrentKind==SdrObjKind::Rectangle && nCurrentPoint>=2) {
1353
0
        pU->CalcRect(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
1354
0
    }
1355
1356
0
    return true;
1357
0
}
1358
1359
bool ImpPathForDragAndCreate::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
1360
0
{
1361
0
    ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
1362
0
    bool bRet = false;
1363
0
    SdrView* pView=rStat.GetView();
1364
0
    bool bIncomp=pView!=nullptr && pView->IsUseIncompatiblePathCreateInterface();
1365
0
    XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1366
0
    sal_uInt16 nCurrentPoint=rXPoly.GetPointCount()-1;
1367
0
    rXPoly[nCurrentPoint]=rStat.GetNow();
1368
0
    if (!pU->bMixedCreate && pU->eStartKind==SdrObjKind::Line) {
1369
0
        if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
1370
0
        bRet = eCmd==SdrCreateCmd::ForceEnd;
1371
0
        if (bRet) {
1372
0
            mbCreating = false;
1373
0
            rStat.SetUser(nullptr);
1374
0
        }
1375
0
        return bRet;
1376
0
    }
1377
1378
0
    if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) {
1379
0
        if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
1380
0
        bRet=eCmd==SdrCreateCmd::ForceEnd;
1381
0
        if (bRet) {
1382
0
            mbCreating=false;
1383
0
            rStat.SetUser(nullptr);
1384
0
        }
1385
0
        return bRet;
1386
0
    }
1387
0
    if (eCmd==SdrCreateCmd::NextPoint || eCmd==SdrCreateCmd::NextObject) {
1388
        // don't allow two consecutive points to occupy the same position
1389
0
        if (nCurrentPoint==0 || rStat.GetNow()!=rXPoly[nCurrentPoint-1]) {
1390
0
            if (bIncomp) {
1391
0
                if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint;
1392
0
                if (IsBezier(pU->eCurrentKind) && nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
1393
0
                    rXPoly.PointsToBezier(nCurrentPoint-3);
1394
0
                    rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
1395
0
                    rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
1396
1397
0
                    if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
1398
0
                        rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
1399
0
                        rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
1400
0
                    }
1401
0
                }
1402
0
            } else {
1403
0
                if (nCurrentPoint==1 && IsBezier(pU->eCurrentKind) && !pU->bBezHasCtrl0) {
1404
0
                    pU->aBezControl0=rStat.GetNow();
1405
0
                    pU->bBezHasCtrl0=true;
1406
0
                    nCurrentPoint--;
1407
0
                }
1408
0
                if (pU->IsFormFlag()) {
1409
0
                    sal_uInt16 nPointCount0=rXPoly.GetPointCount();
1410
0
                    rXPoly.Remove(nCurrentPoint-1,2); // remove last two points and replace by form
1411
0
                    rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
1412
0
                    sal_uInt16 nPointCount1=rXPoly.GetPointCount();
1413
0
                    for (sal_uInt16 i=nPointCount0+1; i<nPointCount1-1; i++) { // to make BckAction work
1414
0
                        if (!rXPoly.IsControl(i)) rStat.NextPoint();
1415
0
                    }
1416
0
                    nCurrentPoint=rXPoly.GetPointCount()-1;
1417
0
                }
1418
0
            }
1419
0
            nCurrentPoint++;
1420
0
            rXPoly[nCurrentPoint]=rStat.GetNow();
1421
0
        }
1422
0
        if (eCmd==SdrCreateCmd::NextObject) {
1423
0
            if (rXPoly.GetPointCount()>=2) {
1424
0
                pU->bBezHasCtrl0=false;
1425
                // only a singular polygon may be opened, so close this
1426
0
                rXPoly[nCurrentPoint]=rXPoly[0];
1427
0
                XPolygon aXP;
1428
0
                aXP[0]=rStat.GetNow();
1429
0
                aPathPolygon.Insert(std::move(aXP));
1430
0
            }
1431
0
        }
1432
0
    }
1433
1434
0
    sal_uInt16 nPolyCount=aPathPolygon.Count();
1435
0
    if (nPolyCount!=0) {
1436
        // delete last point, if necessary
1437
0
        if (eCmd==SdrCreateCmd::ForceEnd) {
1438
0
            XPolygon& rXP=aPathPolygon[nPolyCount-1];
1439
0
            sal_uInt16 nPointCount=rXP.GetPointCount();
1440
0
            if (nPointCount>=2) {
1441
0
                if (!rXP.IsControl(nPointCount-2)) {
1442
0
                    if (rXP[nPointCount-1]==rXP[nPointCount-2]) {
1443
0
                        rXP.Remove(nPointCount-1,1);
1444
0
                    }
1445
0
                } else {
1446
0
                    if (rXP[nPointCount-3]==rXP[nPointCount-2]) {
1447
0
                        rXP.Remove(nPointCount-3,3);
1448
0
                    }
1449
0
                }
1450
0
            }
1451
0
        }
1452
0
        for (sal_uInt16 nPolyNum=nPolyCount; nPolyNum>0;) {
1453
0
            nPolyNum--;
1454
0
            XPolygon& rXP=aPathPolygon[nPolyNum];
1455
0
            sal_uInt16 nPointCount=rXP.GetPointCount();
1456
            // delete polygons with too few points
1457
0
            if (nPolyNum<nPolyCount-1 || eCmd==SdrCreateCmd::ForceEnd) {
1458
0
                if (nPointCount<2) aPathPolygon.Remove(nPolyNum);
1459
0
            }
1460
0
        }
1461
0
    }
1462
0
    pU->ResetFormFlags();
1463
0
    bRet=eCmd==SdrCreateCmd::ForceEnd;
1464
0
    if (bRet) {
1465
0
        mbCreating=false;
1466
0
        rStat.SetUser(nullptr);
1467
0
    }
1468
0
    return bRet;
1469
0
}
1470
1471
bool ImpPathForDragAndCreate::BckCreate(SdrDragStat const & rStat)
1472
0
{
1473
0
    ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
1474
0
    if (aPathPolygon.Count()>0) {
1475
0
        XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1476
0
        sal_uInt16 nCurrentPoint=rXPoly.GetPointCount();
1477
0
        if (nCurrentPoint>0) {
1478
0
            nCurrentPoint--;
1479
            // make the last part of a bezier curve a line
1480
0
            rXPoly.Remove(nCurrentPoint,1);
1481
0
            if (nCurrentPoint>=3 && rXPoly.IsControl(nCurrentPoint-1)) {
1482
                // there should never be a bezier segment at the end, so this is just in case...
1483
0
                rXPoly.Remove(nCurrentPoint-1,1);
1484
0
                if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
1485
0
            }
1486
0
        }
1487
0
        nCurrentPoint=rXPoly.GetPointCount();
1488
0
        if (nCurrentPoint>=4) { // no bezier segment at the end
1489
0
            nCurrentPoint--;
1490
0
            if (rXPoly.IsControl(nCurrentPoint-1)) {
1491
0
                rXPoly.Remove(nCurrentPoint-1,1);
1492
0
                if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
1493
0
            }
1494
0
        }
1495
0
        if (rXPoly.GetPointCount()<2) {
1496
0
            aPathPolygon.Remove(aPathPolygon.Count()-1);
1497
0
        }
1498
0
        if (aPathPolygon.Count()>0) {
1499
0
            XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
1500
0
            sal_uInt16 nLocalCurrentPoint=rLocalXPoly.GetPointCount();
1501
0
            if (nLocalCurrentPoint>0) {
1502
0
                nLocalCurrentPoint--;
1503
0
                rLocalXPoly[nLocalCurrentPoint]=rStat.GetNow();
1504
0
            }
1505
0
        }
1506
0
    }
1507
0
    pU->ResetFormFlags();
1508
0
    return aPathPolygon.Count()!=0;
1509
0
}
1510
1511
void ImpPathForDragAndCreate::BrkCreate(SdrDragStat& rStat)
1512
0
{
1513
0
    aPathPolygon.Clear();
1514
0
    mbCreating=false;
1515
0
    rStat.SetUser(nullptr);
1516
0
}
1517
1518
basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeObjectPolyPolygon(const SdrDragStat& rDrag) const
1519
0
{
1520
0
    basegfx::B2DPolyPolygon aRetval(aPathPolygon.getB2DPolyPolygon());
1521
0
    SdrView* pView = rDrag.GetView();
1522
1523
0
    if(pView && pView->IsUseIncompatiblePathCreateInterface())
1524
0
        return aRetval;
1525
1526
0
    ImpPathCreateUser* pU = static_cast<ImpPathCreateUser*>(rDrag.GetUser());
1527
0
    basegfx::B2DPolygon aNewPolygon(aRetval.count() ? aRetval.getB2DPolygon(aRetval.count() - 1) : basegfx::B2DPolygon());
1528
1529
0
    if(pU->IsFormFlag() && aNewPolygon.count() > 1)
1530
0
    {
1531
        // remove last segment and replace with current
1532
        // do not forget to rescue the previous control point which will be lost when
1533
        // the point it's associated with is removed
1534
0
        const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2);
1535
0
        const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
1536
1537
0
        aNewPolygon.remove(nChangeIndex, 2);
1538
0
        aNewPolygon.append(pU->GetFormPoly().getB2DPolygon());
1539
1540
0
        if(nChangeIndex < aNewPolygon.count())
1541
0
        {
1542
            // if really something was added, set the saved previous control point to the
1543
            // point where it belongs
1544
0
            aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
1545
0
        }
1546
0
    }
1547
1548
0
    if(aRetval.count())
1549
0
    {
1550
0
        aRetval.setB2DPolygon(aRetval.count() - 1, aNewPolygon);
1551
0
    }
1552
0
    else
1553
0
    {
1554
0
        aRetval.append(aNewPolygon);
1555
0
    }
1556
1557
0
    return aRetval;
1558
0
}
1559
1560
basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeDragPolyPolygon(const SdrDragStat& rDrag)
1561
0
{
1562
0
    basegfx::B2DPolyPolygon aRetval;
1563
0
    SdrView* pView = rDrag.GetView();
1564
1565
0
    if(pView && pView->IsUseIncompatiblePathCreateInterface())
1566
0
        return aRetval;
1567
1568
0
    const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
1569
1570
0
    if(pU && pU->bBezier && rDrag.IsMouseDown())
1571
0
    {
1572
        // no more XOR, no need for complicated helplines
1573
0
        basegfx::B2DPolygon aHelpline;
1574
0
        aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
1575
0
        aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
1576
0
        aRetval.append(aHelpline);
1577
0
    }
1578
1579
0
    return aRetval;
1580
0
}
1581
1582
PointerStyle ImpPathForDragAndCreate::GetCreatePointer() const
1583
0
{
1584
0
    switch (meObjectKind) {
1585
0
        case SdrObjKind::Line    : return PointerStyle::DrawLine;
1586
0
        case SdrObjKind::Polygon    : return PointerStyle::DrawPolygon;
1587
0
        case SdrObjKind::PolyLine    : return PointerStyle::DrawPolygon;
1588
0
        case SdrObjKind::PathLine: return PointerStyle::DrawBezier;
1589
0
        case SdrObjKind::PathFill: return PointerStyle::DrawBezier;
1590
0
        case SdrObjKind::FreehandLine: return PointerStyle::DrawFreehand;
1591
0
        case SdrObjKind::FreehandFill: return PointerStyle::DrawFreehand;
1592
0
        case SdrObjKind::PathPoly: return PointerStyle::DrawPolygon;
1593
0
        case SdrObjKind::PathPolyLine: return PointerStyle::DrawPolygon;
1594
0
        default: break;
1595
0
    } // switch
1596
0
    return PointerStyle::Cross;
1597
0
}
1598
1599
SdrPathObjGeoData::SdrPathObjGeoData()
1600
0
    : meKind(SdrObjKind::NONE)
1601
0
{
1602
0
}
1603
1604
SdrPathObjGeoData::~SdrPathObjGeoData()
1605
0
{
1606
0
}
1607
1608
// DrawContact section
1609
1610
std::unique_ptr<sdr::contact::ViewContact> SdrPathObj::CreateObjectSpecificViewContact()
1611
96.6k
{
1612
96.6k
    return std::make_unique<sdr::contact::ViewContactOfSdrPathObj>(*this);
1613
96.6k
}
1614
1615
1616
SdrPathObj::SdrPathObj(
1617
    SdrModel& rSdrModel,
1618
    SdrObjKind eNewKind)
1619
1.83k
:   SdrTextObj(rSdrModel),
1620
1.83k
    meKind(eNewKind)
1621
1.83k
{
1622
1.83k
    m_bClosedObj = IsClosed();
1623
1.83k
}
1624
1625
SdrPathObj::SdrPathObj(SdrModel& rSdrModel, SdrPathObj const & rSource)
1626
4.99k
:   SdrTextObj(rSdrModel, rSource),
1627
4.99k
    meKind(rSource.meKind)
1628
4.99k
{
1629
4.99k
    m_bClosedObj = IsClosed();
1630
4.99k
    maPathPolygon = rSource.GetPathPoly();
1631
4.99k
}
1632
1633
SdrPathObj::SdrPathObj(
1634
    SdrModel& rSdrModel,
1635
    SdrObjKind eNewKind,
1636
    basegfx::B2DPolyPolygon aPathPoly)
1637
89.8k
:   SdrTextObj(rSdrModel),
1638
89.8k
    maPathPolygon(std::move(aPathPoly)),
1639
89.8k
    meKind(eNewKind)
1640
89.8k
{
1641
89.8k
    m_bClosedObj = IsClosed();
1642
89.8k
    ImpForceKind();
1643
89.8k
}
1644
1645
96.6k
SdrPathObj::~SdrPathObj() = default;
1646
1647
static bool lcl_ImpIsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
1648
153k
{
1649
153k
    return (1 == rPolyPolygon.count() && 2 == rPolyPolygon.getB2DPolygon(0).count());
1650
153k
}
1651
1652
static tools::Rectangle lcl_ImpGetBoundRect(const basegfx::B2DPolyPolygon& rPolyPolygon)
1653
137k
{
1654
137k
    basegfx::B2DRange aRange(rPolyPolygon.getB2DRange());
1655
1656
137k
    if (aRange.isEmpty())
1657
60
        return tools::Rectangle();
1658
1659
137k
    return tools::Rectangle(
1660
137k
        basegfx::fround<tools::Long>(aRange.getMinX()), basegfx::fround<tools::Long>(aRange.getMinY()),
1661
137k
        basegfx::fround<tools::Long>(aRange.getMaxX()), basegfx::fround<tools::Long>(aRange.getMaxY()));
1662
137k
}
1663
1664
void SdrPathObj::ImpForceLineAngle()
1665
63.8k
{
1666
63.8k
    if(SdrObjKind::Line != meKind || !lcl_ImpIsLine(GetPathPoly()))
1667
0
        return;
1668
1669
63.8k
    const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0));
1670
63.8k
    const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
1671
63.8k
    const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
1672
63.8k
    const Point aPoint0(basegfx::fround<tools::Long>(aB2DPoint0.getX()),
1673
63.8k
                        basegfx::fround<tools::Long>(aB2DPoint0.getY()));
1674
63.8k
    const Point aPoint1(basegfx::fround<tools::Long>(aB2DPoint1.getX()),
1675
63.8k
                        basegfx::fround<tools::Long>(aB2DPoint1.getY()));
1676
63.8k
    const basegfx::B2DPoint aB2DDelt(aB2DPoint1 - aB2DPoint0);
1677
63.8k
    const Point aDelt(basegfx::fround<tools::Long>(aB2DDelt.getX()),
1678
63.8k
                      basegfx::fround<tools::Long>(aB2DDelt.getY()));
1679
1680
63.8k
    maGeo.m_nRotationAngle=GetAngle(aDelt);
1681
63.8k
    maGeo.m_nShearAngle=0_deg100;
1682
63.8k
    maGeo.RecalcSinCos();
1683
63.8k
    maGeo.RecalcTan();
1684
1685
    // for SdrTextObj, keep aRect up to date
1686
63.8k
    setRectangle(tools::Rectangle::Normalize(aPoint0, aPoint1));
1687
63.8k
}
1688
1689
void SdrPathObj::ImpForceKind()
1690
120k
{
1691
120k
    if (meKind==SdrObjKind::PathPolyLine) meKind=SdrObjKind::PolyLine;
1692
120k
    if (meKind==SdrObjKind::PathPoly) meKind=SdrObjKind::Polygon;
1693
1694
120k
    if(GetPathPoly().areControlPointsUsed())
1695
12.0k
    {
1696
12.0k
        switch (meKind)
1697
12.0k
        {
1698
0
            case SdrObjKind::Line: meKind=SdrObjKind::PathLine; break;
1699
2.83k
            case SdrObjKind::PolyLine: meKind=SdrObjKind::PathLine; break;
1700
5.54k
            case SdrObjKind::Polygon: meKind=SdrObjKind::PathFill; break;
1701
3.68k
            default: break;
1702
12.0k
        }
1703
12.0k
    }
1704
108k
    else
1705
108k
    {
1706
108k
        switch (meKind)
1707
108k
        {
1708
651
            case SdrObjKind::PathLine: meKind=SdrObjKind::PolyLine; break;
1709
0
            case SdrObjKind::FreehandLine: meKind=SdrObjKind::PolyLine; break;
1710
1
            case SdrObjKind::PathFill: meKind=SdrObjKind::Polygon; break;
1711
0
            case SdrObjKind::FreehandFill: meKind=SdrObjKind::Polygon; break;
1712
108k
            default: break;
1713
108k
        }
1714
108k
    }
1715
1716
120k
    if (meKind==SdrObjKind::Line && !lcl_ImpIsLine(GetPathPoly())) meKind=SdrObjKind::PolyLine;
1717
120k
    if (meKind==SdrObjKind::PolyLine && lcl_ImpIsLine(GetPathPoly())) meKind=SdrObjKind::Line;
1718
1719
120k
    m_bClosedObj=IsClosed();
1720
1721
120k
    if (meKind==SdrObjKind::Line)
1722
63.8k
    {
1723
63.8k
        ImpForceLineAngle();
1724
63.8k
    }
1725
56.9k
    else
1726
56.9k
    {
1727
        // #i10659#, for polys with more than 2 points.
1728
1729
        // Here i again need to fix something, because when Path-Polys are Copy-Pasted
1730
        // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
1731
        // a scaling loop started from SdrExchangeView::Paste. In itself, this is not
1732
        // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
1733
        // this is the case, some size needs to be set here in aRect to avoid that the cycle
1734
        // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
1735
        // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
1736
        // from the local Resize() implementation.
1737
1738
        // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
1739
        // text rectangle for the text object itself and methods at SdrTextObj do handle it
1740
        // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
1741
        // which is basically wrong. To make the SdrText methods which deal with aRect directly
1742
        // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
1743
        // command for SdrPathObj. Since adding this update mechanism with #101412# to
1744
        // ImpForceLineAngle() for lines was very successful, i add it to where ImpForceLineAngle()
1745
        // was called, once here below and once on a 2nd place below.
1746
1747
        // #i10659# for SdrTextObj, keep aRect up to date
1748
56.9k
        if(GetPathPoly().count())
1749
56.9k
        {
1750
56.9k
            setRectangle(lcl_ImpGetBoundRect(GetPathPoly()));
1751
56.9k
        }
1752
56.9k
    }
1753
1754
    // #i75974# adapt polygon state to object type. This may include a reinterpretation
1755
    // of a closed geometry as open one, but with identical first and last point
1756
120k
    for(auto& rPolygon : maPathPolygon)
1757
137k
    {
1758
137k
        if(IsClosed() != rPolygon.isClosed())
1759
2.61k
        {
1760
            // #i80213# really change polygon geometry; else e.g. the last point which
1761
            // needs to be identical with the first one will be missing when opening
1762
            // due to OBJ_PATH type
1763
2.61k
            if(rPolygon.isClosed())
1764
2.52k
            {
1765
2.52k
                basegfx::utils::openWithGeometryChange(rPolygon);
1766
2.52k
            }
1767
96
            else
1768
96
            {
1769
96
                basegfx::utils::closeWithGeometryChange(rPolygon);
1770
96
            }
1771
2.61k
        }
1772
137k
    }
1773
120k
}
1774
1775
void SdrPathObj::ImpSetClosed(bool bClose)
1776
0
{
1777
0
    if(bClose)
1778
0
    {
1779
0
        switch (meKind)
1780
0
        {
1781
0
            case SdrObjKind::Line    : meKind=SdrObjKind::Polygon;     break;
1782
0
            case SdrObjKind::PolyLine    : meKind=SdrObjKind::Polygon;     break;
1783
0
            case SdrObjKind::PathLine: meKind=SdrObjKind::PathFill; break;
1784
0
            case SdrObjKind::FreehandLine: meKind=SdrObjKind::FreehandFill; break;
1785
0
            default: break;
1786
0
        }
1787
1788
0
        m_bClosedObj = true;
1789
0
    }
1790
0
    else
1791
0
    {
1792
0
        switch (meKind)
1793
0
        {
1794
0
            case SdrObjKind::Polygon    : meKind=SdrObjKind::PolyLine;     break;
1795
0
            case SdrObjKind::PathFill: meKind=SdrObjKind::PathLine; break;
1796
0
            case SdrObjKind::FreehandFill: meKind=SdrObjKind::FreehandLine; break;
1797
0
            default: break;
1798
0
        }
1799
1800
0
        m_bClosedObj = false;
1801
0
    }
1802
1803
0
    ImpForceKind();
1804
0
}
1805
1806
void SdrPathObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
1807
0
{
1808
0
    rInfo.bNoContortion=false;
1809
1810
0
    bool bCanConv = !HasText() || ImpCanConvTextToCurve();
1811
0
    bool bIsPath = IsBezier();
1812
1813
0
    rInfo.bEdgeRadiusAllowed    = false;
1814
0
    rInfo.bCanConvToPath = bCanConv && !bIsPath;
1815
0
    rInfo.bCanConvToPoly = bCanConv && bIsPath;
1816
0
    rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
1817
0
}
1818
1819
SdrObjKind SdrPathObj::GetObjIdentifier() const
1820
198k
{
1821
198k
    return meKind;
1822
198k
}
1823
1824
rtl::Reference<SdrObject> SdrPathObj::CloneSdrObject(SdrModel& rTargetModel) const
1825
4.99k
{
1826
4.99k
    return new SdrPathObj(rTargetModel, *this);
1827
4.99k
}
1828
1829
OUString SdrPathObj::TakeObjNameSingul() const
1830
0
{
1831
0
    OUString sName;
1832
1833
0
    if(SdrObjKind::Line == meKind)
1834
0
    {
1835
0
        TranslateId pId(STR_ObjNameSingulLINE);
1836
1837
0
        if(lcl_ImpIsLine(GetPathPoly()))
1838
0
        {
1839
0
            const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0));
1840
0
            const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
1841
0
            const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
1842
1843
0
            if(aB2DPoint0 != aB2DPoint1)
1844
0
            {
1845
0
                if(aB2DPoint0.getY() == aB2DPoint1.getY())
1846
0
                {
1847
0
                    pId = STR_ObjNameSingulLINE_Hori;
1848
0
                }
1849
0
                else if(aB2DPoint0.getX() == aB2DPoint1.getX())
1850
0
                {
1851
0
                    pId = STR_ObjNameSingulLINE_Vert;
1852
0
                }
1853
0
                else
1854
0
                {
1855
0
                    const double fDx(fabs(aB2DPoint0.getX() - aB2DPoint1.getX()));
1856
0
                    const double fDy(fabs(aB2DPoint0.getY() - aB2DPoint1.getY()));
1857
1858
0
                    if(fDx == fDy)
1859
0
                    {
1860
0
                        pId = STR_ObjNameSingulLINE_Diag;
1861
0
                    }
1862
0
                }
1863
0
            }
1864
0
        }
1865
1866
0
        sName = SvxResId(pId);
1867
0
    }
1868
0
    else if(SdrObjKind::PolyLine == meKind || SdrObjKind::Polygon == meKind)
1869
0
    {
1870
0
        const bool bClosed(SdrObjKind::Polygon == meKind);
1871
0
        TranslateId pId;
1872
1873
0
        if(mpDAC && mpDAC->IsCreating())
1874
0
        {
1875
0
            if(bClosed)
1876
0
            {
1877
0
                pId = STR_ObjNameSingulPOLY;
1878
0
            }
1879
0
            else
1880
0
            {
1881
0
                pId = STR_ObjNameSingulPLIN;
1882
0
            }
1883
1884
0
            sName = SvxResId(pId);
1885
0
        }
1886
0
        else
1887
0
        {
1888
            // get point count
1889
0
            sal_uInt32 nPointCount(0);
1890
1891
0
            for(auto const& rPolygon : GetPathPoly())
1892
0
            {
1893
0
                nPointCount += rPolygon.count();
1894
0
            }
1895
1896
0
            if(bClosed)
1897
0
            {
1898
0
                pId = STR_ObjNameSingulPOLY_PointCount;
1899
0
            }
1900
0
            else
1901
0
            {
1902
0
                pId = STR_ObjNameSingulPLIN_PointCount;
1903
0
            }
1904
1905
            // #i96537#
1906
0
            sName = SvxResId(pId).replaceFirst("%2", OUString::number(nPointCount));
1907
0
        }
1908
0
    }
1909
0
    else
1910
0
    {
1911
0
        switch (meKind)
1912
0
        {
1913
0
            case SdrObjKind::PathLine: sName = SvxResId(STR_ObjNameSingulPATHLINE); break;
1914
0
            case SdrObjKind::FreehandLine: sName = SvxResId(STR_ObjNameSingulFREELINE); break;
1915
0
            case SdrObjKind::PathFill: sName = SvxResId(STR_ObjNameSingulPATHFILL); break;
1916
0
            case SdrObjKind::FreehandFill: sName = SvxResId(STR_ObjNameSingulFREEFILL); break;
1917
0
            default: break;
1918
0
        }
1919
0
    }
1920
1921
0
    OUString aName(GetName());
1922
0
    if (!aName.isEmpty())
1923
0
        sName += " '" + aName + "'";
1924
1925
0
    return sName;
1926
0
}
1927
1928
OUString SdrPathObj::TakeObjNamePlural() const
1929
0
{
1930
0
    OUString sName;
1931
0
    switch(meKind)
1932
0
    {
1933
0
        case SdrObjKind::Line    : sName=SvxResId(STR_ObjNamePluralLINE    ); break;
1934
0
        case SdrObjKind::PolyLine    : sName=SvxResId(STR_ObjNamePluralPLIN    ); break;
1935
0
        case SdrObjKind::Polygon    : sName=SvxResId(STR_ObjNamePluralPOLY    ); break;
1936
0
        case SdrObjKind::PathLine: sName=SvxResId(STR_ObjNamePluralPATHLINE); break;
1937
0
        case SdrObjKind::FreehandLine: sName=SvxResId(STR_ObjNamePluralFREELINE); break;
1938
0
        case SdrObjKind::PathFill: sName=SvxResId(STR_ObjNamePluralPATHFILL); break;
1939
0
        case SdrObjKind::FreehandFill: sName=SvxResId(STR_ObjNamePluralFREEFILL); break;
1940
0
        default: break;
1941
0
    }
1942
0
    return sName;
1943
0
}
1944
1945
basegfx::B2DPolyPolygon SdrPathObj::TakeXorPoly() const
1946
0
{
1947
0
    return GetPathPoly();
1948
0
}
1949
1950
sal_uInt32 SdrPathObj::GetHdlCount() const
1951
0
{
1952
0
    sal_uInt32 nRetval(0);
1953
1954
0
    for(auto const& rPolygon : GetPathPoly())
1955
0
    {
1956
0
        nRetval += rPolygon.count();
1957
0
    }
1958
1959
0
    return nRetval;
1960
0
}
1961
1962
void SdrPathObj::AddToHdlList(SdrHdlList& rHdlList) const
1963
0
{
1964
    // keep old stuff to be able to keep old SdrHdl stuff, too
1965
0
    const XPolyPolygon aOldPathPolygon(GetPathPoly());
1966
0
    sal_uInt16 nPolyCnt=aOldPathPolygon.Count();
1967
0
    bool bClosed=IsClosed();
1968
0
    sal_uInt16 nIdx=0;
1969
1970
0
    for (sal_uInt16 i=0; i<nPolyCnt; i++) {
1971
0
        const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
1972
0
        sal_uInt16 nPntCnt=rXPoly.GetPointCount();
1973
0
        if (bClosed && nPntCnt>1) nPntCnt--;
1974
1975
0
        for (sal_uInt16 j=0; j<nPntCnt; j++) {
1976
0
            if (rXPoly.GetFlags(j)!=PolyFlags::Control) {
1977
0
                const Point& rPnt=rXPoly[j];
1978
0
                std::unique_ptr<SdrHdl> pHdl(new SdrHdl(rPnt,SdrHdlKind::Poly));
1979
0
                pHdl->SetPolyNum(i);
1980
0
                pHdl->SetPointNum(j);
1981
0
                pHdl->Set1PixMore(j==0);
1982
0
                pHdl->SetSourceHdlNum(nIdx);
1983
0
                nIdx++;
1984
0
                rHdlList.AddHdl(std::move(pHdl));
1985
0
            }
1986
0
        }
1987
0
    }
1988
0
}
1989
1990
void SdrPathObj::AddToPlusHdlList(SdrHdlList& rHdlList, SdrHdl& rHdl) const
1991
0
{
1992
    // keep old stuff to be able to keep old SdrHdl stuff, too
1993
0
    const XPolyPolygon aOldPathPolygon(GetPathPoly());
1994
0
    sal_uInt16 nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
1995
0
    sal_uInt16 nPolyNum = static_cast<sal_uInt16>(rHdl.GetPolyNum());
1996
1997
0
    if (nPolyNum>=aOldPathPolygon.Count())
1998
0
        return;
1999
2000
0
    const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2001
0
    sal_uInt16 nPntMax = rXPoly.GetPointCount();
2002
2003
0
    if (nPntMax<=0)
2004
0
        return;
2005
0
    nPntMax--;
2006
0
    if (nPnt>nPntMax)
2007
0
        return;
2008
2009
    // calculate the number of plus points
2010
0
    sal_uInt16 nCnt = 0;
2011
0
    if (rXPoly.GetFlags(nPnt)!=PolyFlags::Control)
2012
0
    {
2013
0
        if (nPnt==0 && IsClosed())
2014
0
            nPnt=nPntMax;
2015
0
        if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control)
2016
0
            nCnt++;
2017
0
        if (nPnt==nPntMax && IsClosed())
2018
0
            nPnt=0;
2019
0
        if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
2020
0
            nCnt++;
2021
0
    }
2022
2023
    // construct the plus points
2024
0
    for (sal_uInt32 nPlusNum = 0; nPlusNum < nCnt; ++nPlusNum)
2025
0
    {
2026
0
        nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
2027
0
        std::unique_ptr<SdrHdl> pHdl(new SdrHdlBezWgt(&rHdl));
2028
0
        pHdl->SetPolyNum(rHdl.GetPolyNum());
2029
2030
0
        if (nPnt==0 && IsClosed())
2031
0
            nPnt=nPntMax;
2032
0
        if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control && nPlusNum==0)
2033
0
        {
2034
0
            pHdl->SetPos(rXPoly[nPnt-1]);
2035
0
            pHdl->SetPointNum(nPnt-1);
2036
0
        }
2037
0
        else
2038
0
        {
2039
0
            if (nPnt==nPntMax && IsClosed())
2040
0
                nPnt=0;
2041
0
            if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
2042
0
            {
2043
0
                pHdl->SetPos(rXPoly[nPnt+1]);
2044
0
                pHdl->SetPointNum(nPnt+1);
2045
0
            }
2046
0
        }
2047
2048
0
        pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
2049
0
        pHdl->SetPlusHdl(true);
2050
0
        rHdlList.AddHdl(std::move(pHdl));
2051
0
    }
2052
0
}
2053
2054
// tdf#123321: Make sure that SdrPathObj (e.g. line) has big enough extent for
2055
// visibility. This is realised by ensuring GetLogicRect() is the same as
2056
// GetSnapRect() for the SdrPathObj. Other SdrTextObj objects like
2057
// SdrObjCustomShape will still use a different version of this method that
2058
// does not consider the rotation. Otherwise, the rotated SdrObjCustomShape
2059
// would become mistakenly larger after save and reload (tdf#91687).
2060
// The invocation of the GetLogicRect() method that caused tdf#123321 was in
2061
// PlcDrawObj::WritePlc().
2062
const tools::Rectangle &SdrPathObj::GetLogicRect() const
2063
771
{
2064
771
    return GetSnapRect();
2065
771
}
2066
2067
// dragging
2068
2069
bool SdrPathObj::hasSpecialDrag() const
2070
0
{
2071
0
    return true;
2072
0
}
2073
2074
bool SdrPathObj::beginSpecialDrag(SdrDragStat& rDrag) const
2075
0
{
2076
0
    ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2077
2078
0
    return aDragAndCreate.beginPathDrag(rDrag);
2079
0
}
2080
2081
bool SdrPathObj::applySpecialDrag(SdrDragStat& rDrag)
2082
0
{
2083
0
    ImpPathForDragAndCreate aDragAndCreate(*this);
2084
0
    bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
2085
2086
0
    if(bRetval)
2087
0
    {
2088
0
        bRetval = aDragAndCreate.movePathDrag(rDrag);
2089
0
    }
2090
2091
0
    if(bRetval)
2092
0
    {
2093
0
        bRetval = aDragAndCreate.endPathDrag(rDrag);
2094
0
    }
2095
2096
0
    if(bRetval)
2097
0
    {
2098
0
           NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
2099
0
    }
2100
2101
0
    return bRetval;
2102
0
}
2103
2104
OUString SdrPathObj::getSpecialDragComment(const SdrDragStat& rDrag) const
2105
0
{
2106
0
    OUString aRetval;
2107
2108
0
    if(mpDAC)
2109
0
    {
2110
        // #i103058# also get a comment when in creation
2111
0
        const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
2112
2113
0
        if(bCreateComment)
2114
0
        {
2115
0
            aRetval = mpDAC->getSpecialDragComment(rDrag);
2116
0
        }
2117
0
    }
2118
0
    else
2119
0
    {
2120
0
        ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2121
0
        bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
2122
2123
0
        if(bDidWork)
2124
0
        {
2125
0
            aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
2126
0
        }
2127
0
    }
2128
2129
0
    return aRetval;
2130
0
}
2131
2132
basegfx::B2DPolyPolygon SdrPathObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
2133
0
{
2134
0
    basegfx::B2DPolyPolygon aRetval;
2135
0
    ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2136
0
    bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
2137
2138
0
    if(bDidWork)
2139
0
    {
2140
0
        aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
2141
0
    }
2142
2143
0
    return aRetval;
2144
0
}
2145
2146
// creation
2147
2148
bool SdrPathObj::BegCreate(SdrDragStat& rStat)
2149
0
{
2150
0
    mpDAC.reset();
2151
0
    impGetDAC().BegCreate(rStat);
2152
0
    return true;
2153
0
}
2154
2155
bool SdrPathObj::MovCreate(SdrDragStat& rStat)
2156
0
{
2157
0
    return impGetDAC().MovCreate(rStat);
2158
0
}
2159
2160
bool SdrPathObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
2161
0
{
2162
0
    bool bRetval(impGetDAC().EndCreate(rStat, eCmd));
2163
2164
0
    if(bRetval && mpDAC)
2165
0
    {
2166
0
        SetPathPoly(mpDAC->getModifiedPolyPolygon());
2167
2168
        // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
2169
        // to be able to use the type-changing ImpSetClosed method
2170
0
        if(!IsClosedObj())
2171
0
        {
2172
0
            SdrView* pView = rStat.GetView();
2173
2174
0
            if(pView && !pView->IsUseIncompatiblePathCreateInterface())
2175
0
            {
2176
0
                OutputDevice* pOut = pView->GetFirstOutputDevice();
2177
2178
0
                if(pOut)
2179
0
                {
2180
0
                    if(GetPathPoly().count())
2181
0
                    {
2182
0
                        const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
2183
2184
0
                        if(aCandidate.count() > 2)
2185
0
                        {
2186
                            // check distance of first and last point
2187
0
                            const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
2188
0
                            const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
2189
2190
0
                            if(aDistVector.getLength() <= static_cast<double>(nCloseDist))
2191
0
                            {
2192
                                // close it
2193
0
                                ImpSetClosed(true);
2194
0
                            }
2195
0
                        }
2196
0
                    }
2197
0
                }
2198
0
            }
2199
0
        }
2200
2201
0
        mpDAC.reset();
2202
0
    }
2203
2204
0
    return bRetval;
2205
0
}
2206
2207
bool SdrPathObj::BckCreate(SdrDragStat& rStat)
2208
0
{
2209
0
    return impGetDAC().BckCreate(rStat);
2210
0
}
2211
2212
void SdrPathObj::BrkCreate(SdrDragStat& rStat)
2213
0
{
2214
0
    impGetDAC().BrkCreate(rStat);
2215
0
    mpDAC.reset();
2216
0
}
2217
2218
// polygons
2219
2220
basegfx::B2DPolyPolygon SdrPathObj::TakeCreatePoly(const SdrDragStat& rDrag) const
2221
0
{
2222
0
    basegfx::B2DPolyPolygon aRetval;
2223
2224
0
    if(mpDAC)
2225
0
    {
2226
0
        aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2227
0
        aRetval.append(ImpPathForDragAndCreate::TakeDragPolyPolygon(rDrag));
2228
0
    }
2229
2230
0
    return aRetval;
2231
0
}
2232
2233
// during drag or create, allow accessing the so-far created/modified polyPolygon
2234
basegfx::B2DPolyPolygon SdrPathObj::getObjectPolyPolygon(const SdrDragStat& rDrag) const
2235
0
{
2236
0
    basegfx::B2DPolyPolygon aRetval;
2237
2238
0
    if(mpDAC)
2239
0
    {
2240
0
        aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2241
0
    }
2242
2243
0
    return aRetval;
2244
0
}
2245
2246
basegfx::B2DPolyPolygon SdrPathObj::getDragPolyPolygon(const SdrDragStat& rDrag) const
2247
0
{
2248
0
    basegfx::B2DPolyPolygon aRetval;
2249
2250
0
    if(mpDAC)
2251
0
    {
2252
0
        aRetval = ImpPathForDragAndCreate::TakeDragPolyPolygon(rDrag);
2253
0
    }
2254
2255
0
    return aRetval;
2256
0
}
2257
2258
PointerStyle SdrPathObj::GetCreatePointer() const
2259
0
{
2260
0
    return impGetDAC().GetCreatePointer();
2261
0
}
2262
2263
void SdrPathObj::NbcMove(const Size& rSiz)
2264
52.8k
{
2265
52.8k
    maPathPolygon.translate(rSiz.Width(), rSiz.Height());
2266
2267
    // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2268
52.8k
    SdrTextObj::NbcMove(rSiz);
2269
52.8k
}
2270
2271
void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
2272
28.3k
{
2273
28.3k
    const double fResizeX(xFact);
2274
28.3k
    const double fResizeY(yFact);
2275
2276
28.3k
    if(basegfx::fTools::equal(fResizeX, 1.0) && basegfx::fTools::equal(fResizeY, 1.0))
2277
27.1k
    {
2278
        // tdf#106792 avoid numerical unprecisions: If both scale factors are 1.0, do not
2279
        // manipulate at all - that may change maGeo rapidly (and wrongly) in
2280
        // SdrTextObj::NbcResize. Combined with the UNO API trying to not 'apply'
2281
        // a rotation but to manipulate the existing one, this is fatal. So just
2282
        // avoid this error as long as we have to deal with imprecise geometry
2283
        // manipulations
2284
27.1k
        return;
2285
27.1k
    }
2286
2287
1.18k
    basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRef.X(), -rRef.Y()));
2288
1.18k
    aTrans = basegfx::utils::createScaleTranslateB2DHomMatrix(
2289
1.18k
        double(xFact), double(yFact), rRef.X(), rRef.Y()) * aTrans;
2290
1.18k
    maPathPolygon.transform(aTrans);
2291
2292
    // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2293
1.18k
    SdrTextObj::NbcResize(rRef,xFact,yFact);
2294
1.18k
}
2295
2296
void SdrPathObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
2297
11.5k
{
2298
    // Thank JOE, the angles are defined mirrored to the mathematical meanings
2299
11.5k
    const basegfx::B2DHomMatrix aTrans(
2300
11.5k
        basegfx::utils::createRotateAroundPoint(rRef.X(), rRef.Y(), -toRadians(nAngle)));
2301
11.5k
    maPathPolygon.transform(aTrans);
2302
2303
    // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2304
11.5k
    SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
2305
11.5k
}
2306
2307
void SdrPathObj::NbcShear(const Point& rRefPnt, Degree100 nAngle, double fTan, bool bVShear)
2308
0
{
2309
0
    basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt.X(), -rRefPnt.Y()));
2310
2311
0
    if(bVShear)
2312
0
    {
2313
        // Thank JOE, the angles are defined mirrored to the mathematical meanings
2314
0
        aTrans.shearY(-fTan);
2315
0
    }
2316
0
    else
2317
0
    {
2318
0
        aTrans.shearX(-fTan);
2319
0
    }
2320
2321
0
    aTrans.translate(rRefPnt.X(), rRefPnt.Y());
2322
0
    maPathPolygon.transform(aTrans);
2323
2324
    // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2325
0
    SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
2326
0
}
2327
2328
void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
2329
28.4k
{
2330
28.4k
    const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
2331
28.4k
    const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
2332
28.4k
    const double fRot(atan2(fDiffY, fDiffX));
2333
28.4k
    basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt1.X(), -rRefPnt1.Y()));
2334
28.4k
    aTrans.rotate(-fRot);
2335
28.4k
    aTrans.scale(1.0, -1.0);
2336
28.4k
    aTrans.rotate(fRot);
2337
28.4k
    aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
2338
28.4k
    maPathPolygon.transform(aTrans);
2339
2340
    // Do Joe's special handling for lines when mirroring, too
2341
28.4k
    ImpForceKind();
2342
2343
    // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2344
28.4k
    SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
2345
28.4k
}
2346
2347
void SdrPathObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
2348
0
{
2349
0
    if(!maGeo.m_nRotationAngle)
2350
0
    {
2351
0
        rRect = GetSnapRect();
2352
0
    }
2353
0
    else
2354
0
    {
2355
0
        XPolyPolygon aXPP(GetPathPoly());
2356
0
        RotateXPoly(aXPP,Point(),-maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
2357
0
        rRect=aXPP.GetBoundRect();
2358
0
        Point aTmp(rRect.TopLeft());
2359
0
        RotatePoint(aTmp,Point(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
2360
0
        aTmp-=rRect.TopLeft();
2361
0
        rRect.Move(aTmp.X(),aTmp.Y());
2362
0
    }
2363
0
}
2364
2365
void SdrPathObj::RecalcSnapRect()
2366
82.3k
{
2367
82.3k
    if(GetPathPoly().count())
2368
80.5k
    {
2369
80.5k
        maSnapRect = lcl_ImpGetBoundRect(GetPathPoly());
2370
80.5k
    }
2371
82.3k
}
2372
2373
void SdrPathObj::NbcSetSnapRect(const tools::Rectangle& rRect)
2374
28.3k
{
2375
28.3k
    tools::Rectangle aOld(GetSnapRect());
2376
28.3k
    if (aOld.IsEmpty())
2377
4.97k
    {
2378
4.97k
        Fraction aX(1,1);
2379
4.97k
        Fraction aY(1,1);
2380
4.97k
        NbcResize(aOld.TopLeft(), aX, aY);
2381
4.97k
        NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2382
4.97k
        return;
2383
4.97k
    }
2384
2385
    // Take empty into account when calculating scale factors
2386
23.3k
    tools::Long nMulX = rRect.IsWidthEmpty() ? 0 : rRect.Right()  - rRect.Left();
2387
2388
23.3k
    tools::Long nDivX = aOld.Right()   - aOld.Left();
2389
2390
    // Take empty into account when calculating scale factors
2391
23.3k
    tools::Long nMulY = rRect.IsHeightEmpty() ? 0 : rRect.Bottom() - rRect.Top();
2392
2393
23.3k
    tools::Long nDivY = aOld.Bottom()  - aOld.Top();
2394
23.3k
    if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2395
23.3k
    if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2396
23.3k
    if ( nDivX == nMulX ) { nMulX = 1; nDivX = 1; }
2397
23.3k
    if ( nDivY == nMulY ) { nMulY = 1; nDivY = 1; }
2398
23.3k
    Fraction aX(nMulX,nDivX);
2399
23.3k
    Fraction aY(nMulY,nDivY);
2400
23.3k
    NbcResize(aOld.TopLeft(), aX, aY);
2401
23.3k
    NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2402
23.3k
}
2403
2404
sal_uInt32 SdrPathObj::GetSnapPointCount() const
2405
0
{
2406
0
    return GetHdlCount();
2407
0
}
2408
2409
Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
2410
0
{
2411
0
    sal_uInt32 nPoly,nPnt;
2412
0
    if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
2413
0
    {
2414
0
        SAL_WARN("svx", "SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
2415
0
    }
2416
2417
0
    const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
2418
0
    return Point(basegfx::fround<tools::Long>(aB2DPoint.getX()),
2419
0
                 basegfx::fround<tools::Long>(aB2DPoint.getY()));
2420
0
}
2421
2422
bool SdrPathObj::IsPolyObj() const
2423
0
{
2424
0
    return true;
2425
0
}
2426
2427
sal_uInt32 SdrPathObj::GetPointCount() const
2428
20.2k
{
2429
20.2k
    sal_uInt32 nRetval(0);
2430
2431
20.2k
    for(auto const& rPolygon : GetPathPoly())
2432
20.2k
    {
2433
20.2k
        nRetval += rPolygon.count();
2434
20.2k
    }
2435
2436
20.2k
    return nRetval;
2437
20.2k
}
2438
2439
Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
2440
1.15k
{
2441
1.15k
    Point aRetval;
2442
1.15k
    sal_uInt32 nPoly,nPnt;
2443
2444
1.15k
    if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2445
1.15k
    {
2446
1.15k
        const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
2447
1.15k
        const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
2448
1.15k
        aRetval = Point(basegfx::fround<tools::Long>(aPoint.getX()),
2449
1.15k
                        basegfx::fround<tools::Long>(aPoint.getY()));
2450
1.15k
    }
2451
2452
1.15k
    return aRetval;
2453
1.15k
}
2454
2455
void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
2456
0
{
2457
0
    sal_uInt32 nPoly,nPnt;
2458
2459
0
    if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2460
0
        return;
2461
2462
0
    basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
2463
0
    aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
2464
0
    maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
2465
2466
0
    if(meKind==SdrObjKind::Line)
2467
0
    {
2468
0
        ImpForceLineAngle();
2469
0
    }
2470
0
    else
2471
0
    {
2472
0
        if(GetPathPoly().count())
2473
0
        {
2474
            // #i10659# for SdrTextObj, keep aRect up to date
2475
0
            setRectangle(lcl_ImpGetBoundRect(GetPathPoly()));
2476
0
        }
2477
0
    }
2478
2479
0
    SetBoundAndSnapRectsDirty();
2480
0
}
2481
2482
sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, bool bNewObj)
2483
0
{
2484
0
    sal_uInt32 nNewHdl;
2485
2486
0
    if(bNewObj)
2487
0
    {
2488
0
        nNewHdl = NbcInsPoint(rPos, true);
2489
0
    }
2490
0
    else
2491
0
    {
2492
        // look for smallest distance data
2493
0
        const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2494
0
        sal_uInt32 nSmallestPolyIndex(0);
2495
0
        sal_uInt32 nSmallestEdgeIndex(0);
2496
0
        double fSmallestCut;
2497
0
        basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2498
2499
0
        nNewHdl = NbcInsPoint(rPos, false);
2500
0
    }
2501
2502
0
    ImpForceKind();
2503
0
    return nNewHdl;
2504
0
}
2505
2506
sal_uInt32 SdrPathObj::NbcInsPoint(const Point& rPos, bool bNewObj)
2507
0
{
2508
0
    sal_uInt32 nNewHdl;
2509
2510
0
    if(bNewObj)
2511
0
    {
2512
0
        basegfx::B2DPolygon aNewPoly;
2513
0
        const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
2514
0
        aNewPoly.append(aPoint);
2515
0
        aNewPoly.setClosed(IsClosed());
2516
0
        maPathPolygon.append(aNewPoly);
2517
0
        SetBoundAndSnapRectsDirty();
2518
0
        nNewHdl = GetHdlCount();
2519
0
    }
2520
0
    else
2521
0
    {
2522
        // look for smallest distance data
2523
0
        const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2524
0
        sal_uInt32 nSmallestPolyIndex(0);
2525
0
        sal_uInt32 nSmallestEdgeIndex(0);
2526
0
        double fSmallestCut;
2527
0
        basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2528
0
        basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
2529
0
        const bool bBefore(!aCandidate.isClosed() && 0 == nSmallestEdgeIndex && 0.0 == fSmallestCut);
2530
0
        const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2 && 1.0 == fSmallestCut);
2531
2532
0
        if(bBefore)
2533
0
        {
2534
            // before first point
2535
0
            aCandidate.insert(0, aTestPoint);
2536
2537
0
            if(aCandidate.areControlPointsUsed())
2538
0
            {
2539
0
                if(aCandidate.isNextControlPointUsed(1))
2540
0
                {
2541
0
                    aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
2542
0
                    aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
2543
0
                }
2544
0
            }
2545
2546
0
            nNewHdl = 0;
2547
0
        }
2548
0
        else if(bAfter)
2549
0
        {
2550
            // after last point
2551
0
            aCandidate.append(aTestPoint);
2552
2553
0
            if(aCandidate.areControlPointsUsed())
2554
0
            {
2555
0
                if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
2556
0
                {
2557
0
                    aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
2558
0
                    aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
2559
0
                }
2560
0
            }
2561
2562
0
            nNewHdl = aCandidate.count() - 1;
2563
0
        }
2564
0
        else
2565
0
        {
2566
            // in between
2567
0
            bool bSegmentSplit(false);
2568
0
            const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
2569
2570
0
            if(aCandidate.areControlPointsUsed())
2571
0
            {
2572
0
                if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
2573
0
                {
2574
0
                    bSegmentSplit = true;
2575
0
                }
2576
0
            }
2577
2578
0
            if(bSegmentSplit)
2579
0
            {
2580
                // rebuild original segment to get the split data
2581
0
                basegfx::B2DCubicBezier aBezierA, aBezierB;
2582
0
                const basegfx::B2DCubicBezier aBezier(
2583
0
                    aCandidate.getB2DPoint(nSmallestEdgeIndex),
2584
0
                    aCandidate.getNextControlPoint(nSmallestEdgeIndex),
2585
0
                    aCandidate.getPrevControlPoint(nNextIndex),
2586
0
                    aCandidate.getB2DPoint(nNextIndex));
2587
2588
                // split and insert hit point
2589
0
                aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
2590
0
                aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2591
2592
                // since we inserted hit point and not split point, we need to add an offset
2593
                // to the control points to get the C1 continuity we want to achieve
2594
0
                const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
2595
0
                aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
2596
0
                aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
2597
0
                aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
2598
0
                aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
2599
0
            }
2600
0
            else
2601
0
            {
2602
0
                aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2603
0
            }
2604
2605
0
            nNewHdl = nSmallestEdgeIndex + 1;
2606
0
        }
2607
2608
0
        maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
2609
2610
        // create old polygon index from it
2611
0
        for(sal_uInt32 a(0); a < nSmallestPolyIndex; a++)
2612
0
        {
2613
0
            nNewHdl += GetPathPoly().getB2DPolygon(a).count();
2614
0
        }
2615
0
    }
2616
2617
0
    ImpForceKind();
2618
0
    return nNewHdl;
2619
0
}
2620
2621
rtl::Reference<SdrPathObj> SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
2622
0
{
2623
0
    rtl::Reference<SdrPathObj> pNewObj;
2624
0
    const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
2625
0
    sal_uInt32 nPoly, nPnt;
2626
2627
0
    if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
2628
0
    {
2629
0
        if(0 == nPoly)
2630
0
        {
2631
0
            const basegfx::B2DPolygon& aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
2632
0
            const sal_uInt32 nPointCount(aCandidate.count());
2633
2634
0
            if(nPointCount)
2635
0
            {
2636
0
                if(IsClosed())
2637
0
                {
2638
                    // when closed, RipPoint means to open the polygon at the selected point. To
2639
                    // be able to do that, it is necessary to make the selected point the first one
2640
0
                    basegfx::B2DPolygon aNewPolygon(basegfx::utils::makeStartPoint(aCandidate, nPnt));
2641
0
                    SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
2642
0
                    ToggleClosed();
2643
2644
                    // give back new position of old start point (historical reasons)
2645
0
                    rNewPt0Index = (nPointCount - nPnt) % nPointCount;
2646
0
                }
2647
0
                else
2648
0
                {
2649
0
                    if(nPointCount >= 3 && nPnt != 0 && nPnt + 1 < nPointCount)
2650
0
                    {
2651
                        // split in two objects at point nPnt
2652
0
                        basegfx::B2DPolygon aSplitPolyA(aCandidate, 0, nPnt + 1);
2653
0
                        SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
2654
2655
0
                        pNewObj = SdrObject::Clone(*this, getSdrModelFromSdrObject());
2656
0
                        basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
2657
0
                        pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
2658
0
                    }
2659
0
                }
2660
0
            }
2661
0
        }
2662
0
    }
2663
2664
0
    return pNewObj;
2665
0
}
2666
2667
rtl::Reference<SdrObject> SdrPathObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
2668
1.11k
{
2669
    // #i89784# check for FontWork with activated HideContour
2670
1.11k
    const drawinglayer::attribute::SdrTextAttribute aText(
2671
1.11k
        drawinglayer::primitive2d::createNewSdrTextAttribute(GetObjectItemSet(), *getText(0)));
2672
1.11k
    const bool bHideContour(
2673
1.11k
        !aText.isDefault() && !aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour());
2674
2675
1.11k
    rtl::Reference<SdrObject> pRet;
2676
2677
1.11k
    if(!bHideContour)
2678
1.11k
    {
2679
1.11k
        rtl::Reference<SdrPathObj> pPath = ImpConvertMakeObj(GetPathPoly(), IsClosed(), bBezier);
2680
2681
1.11k
        if(pPath->GetPathPoly().areControlPointsUsed())
2682
0
        {
2683
0
            if(!bBezier)
2684
0
            {
2685
                // reduce all bezier curves
2686
0
                pPath->SetPathPoly(basegfx::utils::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
2687
0
            }
2688
0
        }
2689
1.11k
        else
2690
1.11k
        {
2691
1.11k
            if(bBezier)
2692
0
            {
2693
                // create bezier curves
2694
0
                pPath->SetPathPoly(basegfx::utils::expandToCurve(pPath->GetPathPoly()));
2695
0
            }
2696
1.11k
        }
2697
1.11k
        pRet = std::move(pPath);
2698
1.11k
    }
2699
2700
1.11k
    if(bAddText)
2701
1.11k
    {
2702
1.11k
        pRet = ImpConvertAddText(std::move(pRet), bBezier);
2703
1.11k
    }
2704
2705
1.11k
    return pRet;
2706
1.11k
}
2707
2708
std::unique_ptr<SdrObjGeoData> SdrPathObj::NewGeoData() const
2709
0
{
2710
0
    return std::make_unique<SdrPathObjGeoData>();
2711
0
}
2712
2713
void SdrPathObj::SaveGeoData(SdrObjGeoData& rGeo) const
2714
0
{
2715
0
    SdrTextObj::SaveGeoData(rGeo);
2716
0
    SdrPathObjGeoData& rPGeo = static_cast<SdrPathObjGeoData&>( rGeo );
2717
0
    rPGeo.maPathPolygon=GetPathPoly();
2718
0
    rPGeo.meKind=meKind;
2719
0
}
2720
2721
void SdrPathObj::RestoreGeoData(const SdrObjGeoData& rGeo)
2722
0
{
2723
0
    SdrTextObj::RestoreGeoData(rGeo);
2724
0
    const SdrPathObjGeoData& rPGeo=static_cast<const SdrPathObjGeoData&>(rGeo);
2725
0
    maPathPolygon=rPGeo.maPathPolygon;
2726
0
    meKind=rPGeo.meKind;
2727
0
    ImpForceKind(); // to set bClosed (among other things)
2728
0
}
2729
2730
void SdrPathObj::NbcSetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
2731
2.45k
{
2732
2.45k
    if(GetPathPoly() != rPathPoly)
2733
2.45k
    {
2734
2.45k
        maPathPolygon=rPathPoly;
2735
2.45k
        ImpForceKind();
2736
2.45k
        SetBoundAndSnapRectsDirty();
2737
2.45k
    }
2738
2.45k
}
2739
2740
void SdrPathObj::SetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
2741
3.24k
{
2742
3.24k
    if(GetPathPoly() != rPathPoly)
2743
2.45k
    {
2744
2.45k
        tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
2745
2.45k
        NbcSetPathPoly(rPathPoly);
2746
2.45k
        SetChanged();
2747
2.45k
        BroadcastObjectChange();
2748
2.45k
        SendUserCall(SdrUserCallType::Resize,aBoundRect0);
2749
2.45k
    }
2750
3.24k
}
2751
2752
void SdrPathObj::ToggleClosed()
2753
0
{
2754
0
    tools::Rectangle aBoundRect0;
2755
0
    if(m_pUserCall != nullptr)
2756
0
        aBoundRect0 = GetLastBoundRect();
2757
0
    ImpSetClosed(!IsClosed()); // set new ObjKind
2758
0
    ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
2759
0
    SetBoundAndSnapRectsDirty();
2760
0
    SetChanged();
2761
0
    BroadcastObjectChange();
2762
0
    SendUserCall(SdrUserCallType::Resize, aBoundRect0);
2763
0
}
2764
2765
ImpPathForDragAndCreate& SdrPathObj::impGetDAC() const
2766
0
{
2767
0
    if(!mpDAC)
2768
0
    {
2769
0
        const_cast<SdrPathObj*>(this)->mpDAC.reset(new ImpPathForDragAndCreate(*const_cast<SdrPathObj*>(this)));
2770
0
    }
2771
2772
0
    return *mpDAC;
2773
0
}
2774
2775
2776
// transformation interface for StarOfficeAPI. This implements support for
2777
// homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
2778
// moment it contains a shearX, rotation and translation, but for setting all linear
2779
// transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
2780
2781
2782
// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
2783
// with the base geometry and returns TRUE. Otherwise it returns FALSE.
2784
bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
2785
2.32k
{
2786
2.32k
    double fRotate(0.0);
2787
2.32k
    double fShearX(0.0);
2788
2.32k
    basegfx::B2DTuple aScale(1.0, 1.0);
2789
2.32k
    basegfx::B2DTuple aTranslate(0.0, 0.0);
2790
2791
2.32k
    if(GetPathPoly().count())
2792
1.16k
    {
2793
        // copy geometry
2794
1.16k
        basegfx::B2DHomMatrix aMoveToZeroMatrix;
2795
1.16k
        rPolyPolygon = GetPathPoly();
2796
2797
1.16k
        if(SdrObjKind::Line == meKind)
2798
1.07k
        {
2799
            // ignore shear and rotate, just use scale and translate
2800
1.07k
            OSL_ENSURE(GetPathPoly().count() > 0 && GetPathPoly().getB2DPolygon(0).count() > 1, "OBJ_LINE with too few polygons (!)");
2801
            // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2802
            // itself, else this method will no longer return the full polygon information (curve will
2803
            // be lost)
2804
1.07k
            const basegfx::B2DRange aPolyRangeNoCurve(rPolyPolygon.getB2DRange());
2805
1.07k
            aScale = aPolyRangeNoCurve.getRange();
2806
1.07k
            aTranslate = aPolyRangeNoCurve.getMinimum();
2807
2808
            // define matrix for move polygon to zero point
2809
1.07k
            aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2810
1.07k
        }
2811
90
        else
2812
90
        {
2813
90
            if(maGeo.m_nShearAngle || maGeo.m_nRotationAngle)
2814
0
            {
2815
                // get rotate and shear in drawingLayer notation
2816
0
                fRotate = toRadians(maGeo.m_nRotationAngle);
2817
0
                fShearX = toRadians(maGeo.m_nShearAngle);
2818
2819
                // build mathematically correct (negative shear and rotate) object transform
2820
                // containing shear and rotate to extract unsheared, unrotated polygon
2821
0
                basegfx::B2DHomMatrix aObjectMatrix;
2822
0
                aObjectMatrix.shearX(-maGeo.mfTanShearAngle);
2823
0
                aObjectMatrix.rotate(toRadians(36000_deg100 - maGeo.m_nRotationAngle));
2824
2825
                // create inverse from it and back-transform polygon
2826
0
                basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
2827
0
                aInvObjectMatrix.invert();
2828
0
                rPolyPolygon.transform(aInvObjectMatrix);
2829
2830
                // get range from unsheared, unrotated polygon and extract scale and translate.
2831
                // transform topLeft from it back to transformed state to get original
2832
                // topLeft (rotation center)
2833
                // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2834
                // itself, else this method will no longer return the full polygon information (curve will
2835
                // be lost)
2836
0
                const basegfx::B2DRange aCorrectedRangeNoCurve(rPolyPolygon.getB2DRange());
2837
0
                aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
2838
0
                aScale = aCorrectedRangeNoCurve.getRange();
2839
2840
                // define matrix for move polygon to zero point
2841
                // #i112280# Added missing minus for Y-Translation
2842
0
                aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
2843
0
            }
2844
90
            else
2845
90
            {
2846
                // get scale and translate from unsheared, unrotated polygon
2847
                // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2848
                // itself, else this method will no longer return the full polygon information (curve will
2849
                // be lost)
2850
90
                const basegfx::B2DRange aPolyRangeNoCurve(rPolyPolygon.getB2DRange());
2851
90
                if (!aPolyRangeNoCurve.isEmpty())
2852
90
                {
2853
90
                    aScale = aPolyRangeNoCurve.getRange();
2854
90
                    aTranslate = aPolyRangeNoCurve.getMinimum();
2855
2856
                    // define matrix for move polygon to zero point
2857
90
                    aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2858
90
                }
2859
90
            }
2860
90
        }
2861
2862
        // move polygon to zero point with pre-defined matrix
2863
1.16k
        rPolyPolygon.transform(aMoveToZeroMatrix);
2864
1.16k
    }
2865
2866
    // position maybe relative to anchorpos, convert
2867
2.32k
    if( getSdrModelFromSdrObject().IsWriter() )
2868
915
    {
2869
915
        if(GetAnchorPos().X() || GetAnchorPos().Y())
2870
0
        {
2871
0
            aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2872
0
        }
2873
915
    }
2874
2875
    // build return value matrix
2876
2.32k
    rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
2877
2.32k
        aScale,
2878
2.32k
        basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
2879
2.32k
        -fRotate,
2880
2.32k
        aTranslate);
2881
2882
2.32k
    return true;
2883
2.32k
}
2884
2885
void SdrPathObj::SetHandleScale(bool bHandleScale)
2886
707
{
2887
707
    mbHandleScale = bHandleScale;
2888
707
}
2889
2890
// Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
2891
// If it's an SdrPathObj it will use the provided geometry information. The Polygon has
2892
// to use (0,0) as upper left and will be scaled to the given size in the matrix.
2893
void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
2894
2.32k
{
2895
    // break up matrix
2896
2.32k
    basegfx::B2DTuple aScale;
2897
2.32k
    basegfx::B2DTuple aTranslate;
2898
2.32k
    double fRotate, fShearX;
2899
2.32k
    rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
2900
2901
    // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
2902
    // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
2903
2.32k
    if(aScale.getX() < 0.0 && aScale.getY() < 0.0)
2904
0
    {
2905
0
        aScale.setX(fabs(aScale.getX()));
2906
0
        aScale.setY(fabs(aScale.getY()));
2907
0
        fRotate = fmod(fRotate + M_PI, 2 * M_PI);
2908
0
    }
2909
2910
    // copy poly
2911
2.32k
    basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
2912
2913
    // reset object shear and rotations
2914
2.32k
    maGeo.m_nRotationAngle = 0_deg100;
2915
2.32k
    maGeo.RecalcSinCos();
2916
2.32k
    maGeo.m_nShearAngle = 0_deg100;
2917
2.32k
    maGeo.RecalcTan();
2918
2919
2.32k
    if( getSdrModelFromSdrObject().IsWriter() )
2920
915
    {
2921
        // if anchor is used, make position relative to it
2922
915
        if(GetAnchorPos().X() || GetAnchorPos().Y())
2923
0
        {
2924
0
            aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2925
0
        }
2926
915
    }
2927
2928
    // create transformation for polygon, set values at maGeo direct
2929
2.32k
    basegfx::B2DHomMatrix aTransform;
2930
2931
    // #i75086#
2932
    // Given polygon is already scaled (for historical reasons), but not mirrored yet.
2933
    // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
2934
2.32k
    double fScaleX(aScale.getX() < 0.0 ? -1.0 : 1.0);
2935
2.32k
    double fScaleY(aScale.getY() < 0.0 ? -1.0 : 1.0);
2936
2937
    // tdf#98565, tdf#98584. While loading a shape, svg:width and svg:height is used to scale
2938
    // the polygon. But draw:transform might introduce additional scaling factors, which need to
2939
    // be applied to the polygon too, so aScale cannot be ignored while loading.
2940
    // I use "maSnapRect.IsEmpty() && GetPathPoly().count()" to detect this case. Any better
2941
    // idea? The behavior in other cases is the same as it was before this fix.
2942
2.32k
    if (maSnapRect.IsEmpty() && GetPathPoly().count() && mbHandleScale)
2943
707
    {
2944
        // In case of a Writer document, the scaling factors were converted to twips. That is not
2945
        // correct here, because width and height are already in the points coordinates and aScale
2946
        // is no length but only a factor here. Convert back.
2947
707
        if (getSdrModelFromSdrObject().IsWriter())
2948
0
        {
2949
0
            aScale.setX(o3tl::convert(aScale.getX(), o3tl::Length::twip, o3tl::Length::mm100));
2950
0
            aScale.setY(o3tl::convert(aScale.getY(), o3tl::Length::twip, o3tl::Length::mm100));
2951
0
        }
2952
707
        fScaleX *= fabs(aScale.getX());
2953
707
        fScaleY *= fabs(aScale.getY());
2954
707
    }
2955
2956
2.32k
    if (fScaleX != 1.0 || fScaleY != 1.0)
2957
0
        aTransform.scale(fScaleX, fScaleY);
2958
2959
2.32k
    if(!basegfx::fTools::equalZero(fShearX))
2960
0
    {
2961
0
        aTransform.shearX(tan(-atan(fShearX)));
2962
0
        maGeo.m_nShearAngle = Degree100(basegfx::fround(basegfx::rad2deg<100>(atan(fShearX))));
2963
0
        maGeo.RecalcTan();
2964
0
    }
2965
2966
2.32k
    if(!basegfx::fTools::equalZero(fRotate))
2967
0
    {
2968
        // #i78696#
2969
        // fRotate is mathematically correct for linear transformations, so it's
2970
        // the one to use for the geometry change
2971
0
        aTransform.rotate(fRotate);
2972
2973
        // #i78696#
2974
        // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
2975
        // mirrored -> mirror value here
2976
0
        maGeo.m_nRotationAngle = NormAngle36000(Degree100(basegfx::fround(-basegfx::rad2deg<100>(fRotate))));
2977
0
        maGeo.RecalcSinCos();
2978
0
    }
2979
2980
2.32k
    if(!aTranslate.equalZero())
2981
378
    {
2982
        // #i39529# absolute positioning, so get current position (without control points (!))
2983
378
        const basegfx::B2DRange aCurrentRange(aNewPolyPolygon.getB2DRange());
2984
378
        aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
2985
378
    }
2986
2987
    // transform polygon and trigger change
2988
2.32k
    aNewPolyPolygon.transform(aTransform);
2989
2.32k
    SetPathPoly(aNewPolyPolygon);
2990
2.32k
}
2991
2992
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */