Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/svdraw/svdoedge.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 <svx/dialmgr.hxx>
21
#include <svx/strings.hrc>
22
#include <osl/diagnose.h>
23
24
#include <basegfx/matrix/b2dhommatrix.hxx>
25
#include <basegfx/polygon/b2dpolygon.hxx>
26
#include <svl/hint.hxx>
27
28
#include <sdr/contact/viewcontactofsdredgeobj.hxx>
29
#include <sdr/properties/connectorproperties.hxx>
30
#include <svx/compatflags.hxx>
31
#include <svx/sdrhittesthelper.hxx>
32
#include <svx/svddrag.hxx>
33
#include <svx/svddrgmt.hxx>
34
#include <svx/svdhdl.hxx>
35
#include <svx/svdmodel.hxx>
36
#include <svx/svdoedge.hxx>
37
#include <svx/svdopath.hxx>
38
#include <svx/svdpage.hxx>
39
#include <svx/svdpagv.hxx>
40
#include <svx/svdtrans.hxx>
41
#include <svx/svdview.hxx>
42
#include <svx/sxekitm.hxx>
43
#include <svx/sxelditm.hxx>
44
#include <svx/sxenditm.hxx>
45
#include <svx/xpoly.hxx>
46
#include <vcl/ptrstyle.hxx>
47
#include <comphelper/lok.hxx>
48
49
void SdrObjConnection::ResetVars()
50
21.9k
{
51
21.9k
    m_pSdrObj=nullptr;
52
21.9k
    m_nConId=0;
53
21.9k
    m_bBestConn=true;
54
21.9k
    m_bBestVertex=true;
55
21.9k
    m_bAutoVertex=false;
56
21.9k
    m_bAutoCorner=false;
57
21.9k
}
58
59
bool SdrObjConnection::TakeGluePoint(SdrGluePoint& rGP) const
60
275
{
61
275
    bool bRet = false;
62
275
    if (m_pSdrObj!=nullptr) { // one object has to be docked already!
63
275
        if (m_bAutoVertex) {
64
275
            rGP=m_pSdrObj->GetVertexGluePoint(m_nConId);
65
275
            bRet = true;
66
275
        } else if (m_bAutoCorner) {
67
0
            rGP=m_pSdrObj->GetCornerGluePoint(m_nConId);
68
0
            bRet = true;
69
0
        } else {
70
0
            const SdrGluePointList* pGPL=m_pSdrObj->GetGluePointList();
71
0
            if (pGPL!=nullptr) {
72
0
                sal_uInt16 nNum=pGPL->FindGluePoint(m_nConId);
73
0
                if (nNum!=SDRGLUEPOINT_NOTFOUND) {
74
0
                    rGP=(*pGPL)[nNum];
75
0
                    bRet = true;
76
0
                }
77
0
            }
78
0
        }
79
275
    }
80
275
    if (bRet) {
81
275
        Point aPt(rGP.GetAbsolutePos(*m_pSdrObj));
82
275
        aPt+=m_aObjOfs;
83
275
        rGP.SetPos(aPt);
84
275
    }
85
275
    return bRet;
86
275
}
87
88
Point& SdrEdgeInfoRec::ImpGetLineOffsetPoint(SdrEdgeLineCode eLineCode)
89
1.78k
{
90
1.78k
    switch (eLineCode) {
91
0
        case SdrEdgeLineCode::Obj1Line2 : return m_aObj1Line2;
92
0
        case SdrEdgeLineCode::Obj1Line3 : return m_aObj1Line3;
93
0
        case SdrEdgeLineCode::Obj2Line2 : return m_aObj2Line2;
94
0
        case SdrEdgeLineCode::Obj2Line3 : return m_aObj2Line3;
95
1.78k
        case SdrEdgeLineCode::MiddleLine: return m_aMiddleLine;
96
1.78k
    } // switch
97
0
    return m_aMiddleLine;
98
1.78k
}
99
100
sal_uInt16 SdrEdgeInfoRec::ImpGetPolyIdx(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
101
56.8k
{
102
56.8k
    switch (eLineCode) {
103
13.2k
        case SdrEdgeLineCode::Obj1Line2 : return 1;
104
0
        case SdrEdgeLineCode::Obj1Line3 : return 2;
105
13.2k
        case SdrEdgeLineCode::Obj2Line2 : return rXP.GetPointCount()-3;
106
0
        case SdrEdgeLineCode::Obj2Line3 : return rXP.GetPointCount()-4;
107
30.3k
        case SdrEdgeLineCode::MiddleLine: return m_nMiddleLine;
108
56.8k
    } // switch
109
0
    return 0;
110
56.8k
}
111
112
bool SdrEdgeInfoRec::ImpIsHorzLine(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
113
29.3k
{
114
29.3k
    sal_uInt16 nIdx=ImpGetPolyIdx(eLineCode,rXP);
115
29.3k
    bool bHorz=m_nAngle1==0 || m_nAngle1==18000;
116
29.3k
    if (eLineCode==SdrEdgeLineCode::Obj2Line2 || eLineCode==SdrEdgeLineCode::Obj2Line3) {
117
6.64k
        nIdx=rXP.GetPointCount()-nIdx;
118
6.64k
        bHorz=m_nAngle2==0 || m_nAngle2==18000;
119
6.64k
    }
120
29.3k
    if ((nIdx & 1)==1) bHorz=!bHorz;
121
29.3k
    return bHorz;
122
29.3k
}
123
124
void SdrEdgeInfoRec::ImpSetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon& rXP, tools::Long nVal)
125
894
{
126
894
    Point& rPt=ImpGetLineOffsetPoint(eLineCode);
127
894
    if (ImpIsHorzLine(eLineCode,rXP)) rPt.setY(nVal );
128
485
    else rPt.setX(nVal );
129
894
}
130
131
tools::Long SdrEdgeInfoRec::ImpGetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
132
894
{
133
894
    const Point& rPt = const_cast<SdrEdgeInfoRec*>(this)->ImpGetLineOffsetPoint(eLineCode);
134
894
    if (ImpIsHorzLine(eLineCode,rXP))
135
409
        return rPt.Y();
136
485
    else
137
485
        return rPt.X();
138
894
}
139
140
141
// BaseProperties section
142
143
std::unique_ptr<sdr::properties::BaseProperties> SdrEdgeObj::CreateObjectSpecificProperties()
144
10.9k
{
145
10.9k
    return std::make_unique<sdr::properties::ConnectorProperties>(*this);
146
10.9k
}
147
148
149
// DrawContact section
150
151
std::unique_ptr<sdr::contact::ViewContact> SdrEdgeObj::CreateObjectSpecificViewContact()
152
10.9k
{
153
10.9k
    return std::make_unique<sdr::contact::ViewContactOfSdrEdgeObj>(*this);
154
10.9k
}
155
156
157
SdrEdgeObj::SdrEdgeObj(SdrModel& rSdrModel)
158
10.9k
:   SdrTextObj(rSdrModel),
159
10.9k
    m_nNotifyingCount(0),
160
10.9k
    m_bEdgeTrackDirty(false),
161
10.9k
    m_bEdgeTrackUserDefined(false),
162
    // Default is to allow default connects
163
10.9k
    mbSuppressDefaultConnect(false),
164
10.9k
    mbBoundRectCalculationRunning(false),
165
10.9k
    mbSuppressed(false)
166
10.9k
{
167
10.9k
    m_bClosedObj=false;
168
10.9k
    m_bIsEdge=true;
169
10.9k
    m_pEdgeTrack = XPolygon();
170
10.9k
}
171
172
SdrEdgeObj::SdrEdgeObj(SdrModel& rSdrModel, SdrEdgeObj const & rSource)
173
19
:   SdrTextObj(rSdrModel, rSource),
174
19
    m_nNotifyingCount(0),
175
19
    m_bEdgeTrackDirty(false),
176
19
    m_bEdgeTrackUserDefined(false),
177
    // Default is to allow default connects
178
19
    mbSuppressDefaultConnect(false),
179
19
    mbBoundRectCalculationRunning(false),
180
19
    mbSuppressed(false)
181
19
{
182
19
    m_bClosedObj = false;
183
19
    m_bIsEdge = true;
184
19
    m_pEdgeTrack = rSource.m_pEdgeTrack;
185
19
    m_bEdgeTrackDirty=rSource.m_bEdgeTrackDirty;
186
19
    m_aCon1          =rSource.m_aCon1;
187
19
    m_aCon2          =rSource.m_aCon2;
188
19
    m_aCon1.m_pSdrObj=nullptr;
189
19
    m_aCon2.m_pSdrObj=nullptr;
190
19
    m_aEdgeInfo=rSource.m_aEdgeInfo;
191
19
}
192
193
SdrEdgeObj::~SdrEdgeObj()
194
10.9k
{
195
10.9k
    SdrEdgeObj::DisconnectFromNode(true);
196
10.9k
    SdrEdgeObj::DisconnectFromNode(false);
197
10.9k
}
198
199
void SdrEdgeObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
200
12.0k
{
201
    // call parent
202
12.0k
    SdrTextObj::handlePageChange(pOldPage, pNewPage);
203
204
12.0k
    if(nullptr != GetConnection(true).GetSdrObject() || nullptr != GetConnection(false).GetSdrObject())
205
0
    {
206
        // check broadcasters; when we are not inserted we do not need broadcasters
207
        // TTTT not yet added, but keep hint to do this here
208
        // mpCon1->ownerPageChange();
209
        // mpCon2->ownerPageChange();
210
0
    }
211
12.0k
}
212
213
void SdrEdgeObj::ImpSetAttrToEdgeInfo()
214
32.0k
{
215
32.0k
    const SfxItemSet& rSet = GetObjectItemSet();
216
32.0k
    SdrEdgeKind eKind = rSet.Get(SDRATTR_EDGEKIND).GetValue();
217
32.0k
    sal_Int32 nVal1 = rSet.Get(SDRATTR_EDGELINE1DELTA).GetValue();
218
32.0k
    sal_Int32 nVal2 = rSet.Get(SDRATTR_EDGELINE2DELTA).GetValue();
219
32.0k
    sal_Int32 nVal3 = rSet.Get(SDRATTR_EDGELINE3DELTA).GetValue();
220
221
32.0k
    if(eKind == SdrEdgeKind::OrthoLines || eKind == SdrEdgeKind::Bezier)
222
26.9k
    {
223
26.9k
        sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
224
26.9k
        sal_uInt16 n = 0;
225
226
26.9k
        if(m_aEdgeInfo.m_nObj1Lines >= 2 && n < 3)
227
0
        {
228
0
            m_aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj1Line2, *m_pEdgeTrack, nVals[n]);
229
0
            n++;
230
0
        }
231
232
26.9k
        if(m_aEdgeInfo.m_nObj1Lines >= 3 && n < 3)
233
0
        {
234
0
            m_aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj1Line3, *m_pEdgeTrack, nVals[n]);
235
0
            n++;
236
0
        }
237
238
26.9k
        if(m_aEdgeInfo.m_nMiddleLine != 0xFFFF && n < 3)
239
894
        {
240
894
            m_aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::MiddleLine, *m_pEdgeTrack, nVals[n]);
241
894
            n++;
242
894
        }
243
244
26.9k
        if(m_aEdgeInfo.m_nObj2Lines >= 3 && n < 3)
245
0
        {
246
0
            m_aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj2Line3, *m_pEdgeTrack, nVals[n]);
247
0
            n++;
248
0
        }
249
250
26.9k
        if(m_aEdgeInfo.m_nObj2Lines >= 2 && n < 3)
251
0
        {
252
0
            m_aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj2Line2, *m_pEdgeTrack, nVals[n]);
253
0
            n++;
254
0
        }
255
256
        // Do not overwrite existing value with default. ImpSetAttrToEdgeInfo() is called several
257
        // times with a set, that does not have SDRATTR_EDGEOOXMLCURVE item.
258
26.9k
        if (rSet.HasItem(SDRATTR_EDGEOOXMLCURVE))
259
840
            m_aEdgeInfo.m_bUseOOXMLCurve = rSet.Get(SDRATTR_EDGEOOXMLCURVE).GetValue();
260
26.9k
    }
261
5.04k
    else if(eKind == SdrEdgeKind::ThreeLines)
262
0
    {
263
0
        bool bHor1 = m_aEdgeInfo.m_nAngle1 == 0 || m_aEdgeInfo.m_nAngle1 == 18000;
264
0
        bool bHor2 = m_aEdgeInfo.m_nAngle2 == 0 || m_aEdgeInfo.m_nAngle2 == 18000;
265
266
0
        if(bHor1)
267
0
        {
268
0
            m_aEdgeInfo.m_aObj1Line2.setX( nVal1 );
269
0
        }
270
0
        else
271
0
        {
272
0
            m_aEdgeInfo.m_aObj1Line2.setY( nVal1 );
273
0
        }
274
275
0
        if(bHor2)
276
0
        {
277
0
            m_aEdgeInfo.m_aObj2Line2.setX( nVal2 );
278
0
        }
279
0
        else
280
0
        {
281
0
            m_aEdgeInfo.m_aObj2Line2.setY( nVal2 );
282
0
        }
283
0
    }
284
285
32.0k
    ImpDirtyEdgeTrack();
286
32.0k
}
287
288
void SdrEdgeObj::ImpSetEdgeInfoToAttr()
289
3.82k
{
290
3.82k
    const SfxItemSet& rSet = GetObjectItemSet();
291
3.82k
    SdrEdgeKind eKind = rSet.Get(SDRATTR_EDGEKIND).GetValue();
292
3.82k
    sal_Int32 nValCnt = rSet.Get(SDRATTR_EDGELINEDELTACOUNT).GetValue();
293
3.82k
    sal_Int32 nVal1 = rSet.Get(SDRATTR_EDGELINE1DELTA).GetValue();
294
3.82k
    sal_Int32 nVal2 = rSet.Get(SDRATTR_EDGELINE2DELTA).GetValue();
295
3.82k
    sal_Int32 nVal3 = rSet.Get(SDRATTR_EDGELINE3DELTA).GetValue();
296
3.82k
    sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
297
3.82k
    sal_uInt16 n = 0;
298
299
3.82k
    if(eKind == SdrEdgeKind::OrthoLines || eKind == SdrEdgeKind::Bezier)
300
2.37k
    {
301
2.37k
        if(m_aEdgeInfo.m_nObj1Lines >= 2 && n < 3)
302
0
        {
303
0
            nVals[n] = m_aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj1Line2, *m_pEdgeTrack);
304
0
            n++;
305
0
        }
306
307
2.37k
        if(m_aEdgeInfo.m_nObj1Lines >= 3 && n < 3)
308
0
        {
309
0
            nVals[n] = m_aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj1Line3, *m_pEdgeTrack);
310
0
            n++;
311
0
        }
312
313
2.37k
        if(m_aEdgeInfo.m_nMiddleLine != 0xFFFF && n < 3)
314
894
        {
315
894
            nVals[n] = m_aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::MiddleLine, *m_pEdgeTrack);
316
894
            n++;
317
894
        }
318
319
2.37k
        if(m_aEdgeInfo.m_nObj2Lines >= 3 && n < 3)
320
0
        {
321
0
            nVals[n] = m_aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj2Line3, *m_pEdgeTrack);
322
0
            n++;
323
0
        }
324
325
2.37k
        if(m_aEdgeInfo.m_nObj2Lines >= 2 && n < 3)
326
0
        {
327
0
            nVals[n] = m_aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj2Line2, *m_pEdgeTrack);
328
0
            n++;
329
0
        }
330
2.37k
    }
331
1.45k
    else if(eKind == SdrEdgeKind::ThreeLines)
332
0
    {
333
0
        bool bHor1 = m_aEdgeInfo.m_nAngle1 == 0 || m_aEdgeInfo.m_nAngle1 == 18000;
334
0
        bool bHor2 = m_aEdgeInfo.m_nAngle2 == 0 || m_aEdgeInfo.m_nAngle2 == 18000;
335
336
0
        n = 2;
337
0
        nVals[0] = bHor1 ? m_aEdgeInfo.m_aObj1Line2.X() : m_aEdgeInfo.m_aObj1Line2.Y();
338
0
        nVals[1] = bHor2 ? m_aEdgeInfo.m_aObj2Line2.X() : m_aEdgeInfo.m_aObj2Line2.Y();
339
0
    }
340
341
3.82k
    if(!(n != nValCnt || nVals[0] != nVal1 || nVals[1] != nVal2 || nVals[2] != nVal3))
342
2.09k
        return;
343
344
    // Here no more notifying is necessary, just local changes are OK.
345
1.73k
    if(n != nValCnt)
346
1.73k
    {
347
1.73k
        GetProperties().SetObjectItemDirect(SdrEdgeLineDeltaCountItem(n));
348
1.73k
    }
349
350
1.73k
    if(nVals[0] != nVal1)
351
0
    {
352
0
        GetProperties().SetObjectItemDirect(makeSdrEdgeLine1DeltaItem(nVals[0]));
353
0
    }
354
355
1.73k
    if(nVals[1] != nVal2)
356
0
    {
357
0
        GetProperties().SetObjectItemDirect(makeSdrEdgeLine2DeltaItem(nVals[1]));
358
0
    }
359
360
1.73k
    if(nVals[2] != nVal3)
361
0
    {
362
0
        GetProperties().SetObjectItemDirect(makeSdrEdgeLine3DeltaItem(nVals[2]));
363
0
    }
364
365
1.73k
    if(n < 3)
366
1.73k
    {
367
1.73k
        GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE3DELTA);
368
1.73k
    }
369
370
1.73k
    if(n < 2)
371
1.73k
    {
372
1.73k
        GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE2DELTA);
373
1.73k
    }
374
375
1.73k
    if(n < 1)
376
840
    {
377
840
        GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE1DELTA);
378
840
    }
379
380
1.73k
    GetProperties().SetObjectItemDirect(
381
1.73k
        SfxBoolItem(SDRATTR_EDGEOOXMLCURVE, m_aEdgeInfo.m_bUseOOXMLCurve));
382
1.73k
}
383
384
void SdrEdgeObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
385
0
{
386
    // #i54102# allow rotation, mirror and shear
387
0
    rInfo.bRotateFreeAllowed = true;
388
0
    rInfo.bRotate90Allowed = true;
389
0
    rInfo.bMirrorFreeAllowed = true;
390
0
    rInfo.bMirror45Allowed = true;
391
0
    rInfo.bMirror90Allowed = true;
392
0
    rInfo.bTransparenceAllowed = false;
393
0
    rInfo.bShearAllowed = true;
394
0
    rInfo.bEdgeRadiusAllowed = false;
395
0
    bool bCanConv=!HasText() || ImpCanConvTextToCurve();
396
0
    rInfo.bCanConvToPath=bCanConv;
397
0
    rInfo.bCanConvToPoly=bCanConv;
398
0
    rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
399
0
}
400
401
SdrObjKind SdrEdgeObj::GetObjIdentifier() const
402
74.6k
{
403
74.6k
    return SdrObjKind::Edge;
404
74.6k
}
405
406
const tools::Rectangle& SdrEdgeObj::GetCurrentBoundRect() const
407
2.75k
{
408
2.75k
    if(m_bEdgeTrackDirty)
409
1.48k
    {
410
1.48k
        const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
411
1.48k
    }
412
413
2.75k
    return SdrTextObj::GetCurrentBoundRect();
414
2.75k
}
415
416
const tools::Rectangle& SdrEdgeObj::GetSnapRect() const
417
12.3k
{
418
12.3k
    if(m_bEdgeTrackDirty)
419
709
    {
420
709
        const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
421
709
    }
422
423
12.3k
    return SdrTextObj::GetSnapRect();
424
12.3k
}
425
426
void SdrEdgeObj::RecalcSnapRect()
427
5.45k
{
428
5.45k
    maSnapRect=m_pEdgeTrack->GetBoundRect();
429
5.45k
}
430
431
void SdrEdgeObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
432
0
{
433
0
    rRect=GetSnapRect();
434
0
}
435
436
SdrGluePoint SdrEdgeObj::GetVertexGluePoint(sal_uInt16 nNum) const
437
0
{
438
0
    Point aPt;
439
0
    sal_uInt16 nPointCount=m_pEdgeTrack->GetPointCount();
440
0
    if (nPointCount>0)
441
0
    {
442
0
        Point aOfs = GetSnapRect().Center();
443
0
        if (nNum==2 && GetConnectedNode(true)==nullptr) aPt=(*m_pEdgeTrack)[0];
444
0
        else if (nNum==3 && GetConnectedNode(false)==nullptr) aPt=(*m_pEdgeTrack)[nPointCount-1];
445
0
        else {
446
0
            if ((nPointCount & 1) ==1) {
447
0
                aPt=(*m_pEdgeTrack)[nPointCount/2];
448
0
            } else {
449
0
                Point aPt1((*m_pEdgeTrack)[nPointCount/2-1]);
450
0
                Point aPt2((*m_pEdgeTrack)[nPointCount/2]);
451
0
                aPt1+=aPt2;
452
0
                aPt1.setX( aPt1.X() / 2 );
453
0
                aPt1.setY( aPt1.Y() / 2 );
454
0
                aPt=aPt1;
455
0
            }
456
0
        }
457
0
        aPt-=aOfs;
458
0
    }
459
0
    SdrGluePoint aGP(aPt);
460
0
    aGP.SetPercent(false);
461
0
    return aGP;
462
0
}
463
464
SdrGluePoint SdrEdgeObj::GetCornerGluePoint(sal_uInt16 nNum) const
465
0
{
466
0
    return GetVertexGluePoint(nNum);
467
0
}
468
469
const SdrGluePointList* SdrEdgeObj::GetGluePointList() const
470
2.71k
{
471
2.71k
    return nullptr; // no user defined gluepoints for connectors
472
2.71k
}
473
474
SdrGluePointList* SdrEdgeObj::ForceGluePointList()
475
26
{
476
26
    return nullptr; // no user defined gluepoints for connectors
477
26
}
478
479
void SdrEdgeObj::ConnectToNode(bool bTail1, SdrObject* pObj)
480
25.0k
{
481
25.0k
    SdrObjConnection& rCon=GetConnection(bTail1);
482
25.0k
    DisconnectFromNode(bTail1);
483
25.0k
    if (pObj!=nullptr) {
484
6.42k
        pObj->AddListener(*this);
485
6.42k
        rCon.m_pSdrObj=pObj;
486
487
        // #i120437# If connection is set, reset bEdgeTrackUserDefined
488
6.42k
        m_bEdgeTrackUserDefined = false;
489
490
6.42k
        ImpDirtyEdgeTrack();
491
6.42k
    }
492
25.0k
}
493
494
void SdrEdgeObj::DisconnectFromNode(bool bTail1)
495
46.9k
{
496
46.9k
    SdrObjConnection& rCon=GetConnection(bTail1);
497
46.9k
    if (rCon.m_pSdrObj!=nullptr) {
498
4.66k
        rCon.m_pSdrObj->RemoveListener(*this);
499
4.66k
        rCon.m_pSdrObj=nullptr;
500
4.66k
    }
501
46.9k
}
502
503
SdrObject* SdrEdgeObj::GetConnectedNode(bool bTail1) const
504
72
{
505
72
    SdrObject* pObj(GetConnection(bTail1).m_pSdrObj);
506
507
72
    if(nullptr != pObj
508
0
        && (pObj->getSdrPageFromSdrObject() != getSdrPageFromSdrObject() || !pObj->IsInserted()))
509
0
    {
510
0
        pObj = nullptr;
511
0
    }
512
513
72
    return pObj;
514
72
}
515
516
bool SdrEdgeObj::CheckNodeConnection(bool bTail1) const
517
0
{
518
0
    bool bRet = false;
519
0
    const SdrObjConnection& rCon=GetConnection(bTail1);
520
0
    sal_uInt16 nPointCount=m_pEdgeTrack->GetPointCount();
521
522
0
    if(nullptr != rCon.m_pSdrObj && rCon.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject() && 0 != nPointCount)
523
0
    {
524
0
        const SdrGluePointList* pGPL=rCon.m_pSdrObj->GetGluePointList();
525
0
        sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
526
0
        sal_uInt16 nGesAnz=nGluePointCnt+8;
527
0
        Point aTail(bTail1 ? (*m_pEdgeTrack)[0] : (*m_pEdgeTrack)[sal_uInt16(nPointCount-1)]);
528
0
        for (sal_uInt16 i=0; i<nGesAnz && !bRet; i++) {
529
0
            if (i<nGluePointCnt) { // UserDefined
530
0
                bRet=aTail==(*pGPL)[i].GetAbsolutePos(*rCon.m_pSdrObj);
531
0
            } else if (i<nGluePointCnt+4) { // Vertex
532
0
                SdrGluePoint aPt(rCon.m_pSdrObj->GetVertexGluePoint(i-nGluePointCnt));
533
0
                bRet=aTail==aPt.GetAbsolutePos(*rCon.m_pSdrObj);
534
0
            } else {                  // Corner
535
0
                SdrGluePoint aPt(rCon.m_pSdrObj->GetCornerGluePoint(i-nGluePointCnt-4));
536
0
                bRet=aTail==aPt.GetAbsolutePos(*rCon.m_pSdrObj);
537
0
            }
538
0
        }
539
0
    }
540
0
    return bRet;
541
0
}
542
543
void SdrEdgeObj::ImpSetTailPoint(bool bTail1, const Point& rPt)
544
3.24k
{
545
3.24k
    sal_uInt16 nPointCount=m_pEdgeTrack->GetPointCount();
546
3.24k
    if (nPointCount==0) {
547
1.62k
        (*m_pEdgeTrack)[0]=rPt;
548
1.62k
        (*m_pEdgeTrack)[1]=rPt;
549
1.62k
    } else if (nPointCount==1) {
550
0
        if (!bTail1) (*m_pEdgeTrack)[1]=rPt;
551
0
        else { (*m_pEdgeTrack)[1]=(*m_pEdgeTrack)[0]; (*m_pEdgeTrack)[0]=rPt; }
552
1.62k
    } else {
553
1.62k
        if (!bTail1) (*m_pEdgeTrack)[sal_uInt16(nPointCount-1)]=rPt;
554
1.62k
        else (*m_pEdgeTrack)[0]=rPt;
555
1.62k
    }
556
3.24k
    ImpRecalcEdgeTrack();
557
3.24k
    SetBoundAndSnapRectsDirty();
558
3.24k
}
559
560
void SdrEdgeObj::ImpDirtyEdgeTrack()
561
44.8k
{
562
44.8k
    if ( !m_bEdgeTrackUserDefined || !getSdrModelFromSdrObject().isLocked() )
563
35.6k
        m_bEdgeTrackDirty = true;
564
44.8k
}
565
566
void SdrEdgeObj::ImpUndirtyEdgeTrack()
567
18.6k
{
568
18.6k
    if (m_bEdgeTrackDirty && getSdrModelFromSdrObject().isLocked())
569
18.5k
    {
570
18.5k
        ImpRecalcEdgeTrack();
571
18.5k
    }
572
18.6k
}
573
574
void SdrEdgeObj::ImpRecalcEdgeTrack()
575
36.8k
{
576
    // #i120437# if bEdgeTrackUserDefined, do not recalculate
577
36.8k
    if(m_bEdgeTrackUserDefined)
578
18
    {
579
18
        return;
580
18
    }
581
582
    // #i120437# also not when model locked during import, but remember
583
36.8k
    if(getSdrModelFromSdrObject().isLocked())
584
33.0k
    {
585
33.0k
        mbSuppressed = true;
586
33.0k
        return;
587
33.0k
    }
588
589
    // #i110649#
590
3.82k
    if(mbBoundRectCalculationRunning)
591
0
    {
592
        // This object is involved into another ImpRecalcEdgeTrack() call
593
        // from another SdrEdgeObj. Do not calculate again to avoid loop.
594
        // Also, do not change bEdgeTrackDirty so that it gets recalculated
595
        // later at the first non-looping call.
596
0
    }
597
3.82k
    else
598
3.82k
    {
599
        // To not run in a depth loop, use a coloring algorithm on
600
        // SdrEdgeObj BoundRect calculations
601
3.82k
        mbBoundRectCalculationRunning = true;
602
603
3.82k
        if(mbSuppressed)
604
314
        {
605
            // #i123048# If layouting was ever suppressed, it needs to be done once
606
            // and the attr need to be set at EdgeInfo, else these attr *will be lost*
607
            // in the following call to ImpSetEdgeInfoToAttr() since they were never
608
            // set before (!)
609
314
            *m_pEdgeTrack=ImpCalcEdgeTrack(*m_pEdgeTrack,m_aCon1,m_aCon2,&m_aEdgeInfo);
610
314
            ImpSetAttrToEdgeInfo();
611
314
            mbSuppressed = false;
612
314
        }
613
614
3.82k
        tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
615
3.82k
        SetBoundAndSnapRectsDirty();
616
3.82k
        *m_pEdgeTrack=ImpCalcEdgeTrack(*m_pEdgeTrack,m_aCon1,m_aCon2,&m_aEdgeInfo);
617
3.82k
        ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
618
3.82k
        m_bEdgeTrackDirty=false;
619
620
        // Only redraw here, no object change
621
3.82k
        ActionChanged();
622
623
3.82k
        SendUserCall(SdrUserCallType::Resize,aBoundRect0);
624
625
3.82k
        mbBoundRectCalculationRunning = false;
626
3.82k
    }
627
3.82k
}
628
629
SdrEscapeDirection SdrEdgeObj::ImpCalcEscAngle(SdrObject const * pObj, const Point& rPt)
630
275
{
631
275
    if (pObj==nullptr) return SdrEscapeDirection::ALL;
632
275
    tools::Rectangle aR(pObj->GetSnapRect());
633
275
    tools::Long dxl=rPt.X()-aR.Left();
634
275
    tools::Long dyo=rPt.Y()-aR.Top();
635
275
    tools::Long dxr=aR.Right()-rPt.X();
636
275
    tools::Long dyu=aR.Bottom()-rPt.Y();
637
275
    bool bxMitt=std::abs(dxl-dxr)<2;
638
275
    bool byMitt=std::abs(dyo-dyu)<2;
639
275
    tools::Long dx=std::min(dxl,dxr);
640
275
    tools::Long dy=std::min(dyo,dyu);
641
275
    bool bDiag=std::abs(dx-dy)<2;
642
275
    if (bxMitt && byMitt) return SdrEscapeDirection::ALL; // in the center
643
275
    if (bDiag) {  // diagonally
644
0
        SdrEscapeDirection nRet=SdrEscapeDirection::SMART;
645
0
        if (byMitt) nRet|=SdrEscapeDirection::VERT;
646
0
        if (bxMitt) nRet|=SdrEscapeDirection::HORZ;
647
0
        if (dxl<dxr) { // left
648
0
            if (dyo<dyu) nRet|=SdrEscapeDirection::LEFT | SdrEscapeDirection::TOP;
649
0
            else nRet|=SdrEscapeDirection::LEFT | SdrEscapeDirection::BOTTOM;
650
0
        } else {       // right
651
0
            if (dyo<dyu) nRet|=SdrEscapeDirection::RIGHT | SdrEscapeDirection::TOP;
652
0
            else nRet|=SdrEscapeDirection::RIGHT | SdrEscapeDirection::BOTTOM;
653
0
        }
654
0
        return nRet;
655
0
    }
656
275
    if (dx<dy) { // horizontal
657
209
        if (bxMitt) return SdrEscapeDirection::HORZ;
658
209
        if (dxl<dxr) return SdrEscapeDirection::LEFT;
659
106
        else return SdrEscapeDirection::RIGHT;
660
209
    } else {     // vertical
661
66
        if (byMitt) return SdrEscapeDirection::VERT;
662
66
        if (dyo<dyu) return SdrEscapeDirection::TOP;
663
33
        else return SdrEscapeDirection::BOTTOM;
664
66
    }
665
275
}
666
667
XPolygon SdrEdgeObj::ImpCalcObjToCenter(const Point& rStPt, tools::Long nEscAngle, const tools::Rectangle& rRect, const Point& rMeeting)
668
77.6k
{
669
77.6k
    XPolygon aXP;
670
77.6k
    aXP.Insert(XPOLY_APPEND,rStPt,PolyFlags::Normal);
671
77.6k
    bool bRts=nEscAngle==0;
672
77.6k
    bool bObn=nEscAngle==9000;
673
77.6k
    bool bLks=nEscAngle==18000;
674
77.6k
    bool bUnt=nEscAngle==27000;
675
676
77.6k
    Point aP1(rStPt); // mandatory difference first,...
677
77.6k
    if (bLks) aP1.setX(rRect.Left() );
678
77.6k
    if (bRts) aP1.setX(rRect.Right() );
679
77.6k
    if (bObn) aP1.setY(rRect.Top() );
680
77.6k
    if (bUnt) aP1.setY(rRect.Bottom() );
681
682
77.6k
    Point aP2(aP1); // ...now increase to Meeting height, if necessary
683
77.6k
    if (bLks && rMeeting.X()<=aP2.X()) aP2.setX(rMeeting.X() );
684
77.6k
    if (bRts && rMeeting.X()>=aP2.X()) aP2.setX(rMeeting.X() );
685
77.6k
    if (bObn && rMeeting.Y()<=aP2.Y()) aP2.setY(rMeeting.Y() );
686
77.6k
    if (bUnt && rMeeting.Y()>=aP2.Y()) aP2.setY(rMeeting.Y() );
687
77.6k
    aXP.Insert(XPOLY_APPEND,aP2,PolyFlags::Normal);
688
689
77.6k
    Point aP3(aP2);
690
77.6k
    if ((bLks && rMeeting.X()>aP2.X()) || (bRts && rMeeting.X()<aP2.X())) { // around
691
4.27k
        if (rMeeting.Y()<aP2.Y()) {
692
1.89k
            aP3.setY(rRect.Top() );
693
1.89k
            if (rMeeting.Y()<aP3.Y()) aP3.setY(rMeeting.Y() );
694
2.37k
        } else {
695
2.37k
            aP3.setY(rRect.Bottom() );
696
2.37k
            if (rMeeting.Y()>aP3.Y()) aP3.setY(rMeeting.Y() );
697
2.37k
        }
698
4.27k
        aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
699
4.27k
        if (aP3.Y()!=rMeeting.Y()) {
700
0
            aP3.setX(rMeeting.X() );
701
0
            aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
702
0
        }
703
4.27k
    }
704
77.6k
    if ((bObn && rMeeting.Y()>aP2.Y()) || (bUnt && rMeeting.Y()<aP2.Y())) { // around
705
5.23k
        if (rMeeting.X()<aP2.X()) {
706
1.89k
            aP3.setX(rRect.Left() );
707
1.89k
            if (rMeeting.X()<aP3.X()) aP3.setX(rMeeting.X() );
708
3.33k
        } else {
709
3.33k
            aP3.setX(rRect.Right() );
710
3.33k
            if (rMeeting.X()>aP3.X()) aP3.setX(rMeeting.X() );
711
3.33k
        }
712
5.23k
        aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
713
5.23k
        if (aP3.X()!=rMeeting.X()) {
714
0
            aP3.setY(rMeeting.Y() );
715
0
            aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
716
0
        }
717
5.23k
    }
718
#ifdef DBG_UTIL
719
    if (aXP.GetPointCount()>4) {
720
        OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
721
    }
722
#endif
723
77.6k
    return aXP;
724
77.6k
}
725
726
XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const XPolygon& rTrack0, SdrObjConnection& rCon1, SdrObjConnection& rCon2, SdrEdgeInfoRec* pInfo) const
727
4.14k
{
728
4.14k
    Point aPt1,aPt2;
729
4.14k
    SdrGluePoint aGP1,aGP2;
730
4.14k
    SdrEscapeDirection nEsc1=SdrEscapeDirection::ALL,nEsc2=SdrEscapeDirection::ALL;
731
4.14k
    tools::Rectangle aBoundRect1;
732
4.14k
    tools::Rectangle aBoundRect2;
733
4.14k
    tools::Rectangle aBewareRect1;
734
4.14k
    tools::Rectangle aBewareRect2;
735
    // first, get the old corner points
736
4.14k
    if (rTrack0.GetPointCount()!=0) {
737
4.14k
        aPt1=rTrack0[0];
738
4.14k
        sal_uInt16 nSiz=rTrack0.GetPointCount();
739
4.14k
        nSiz--;
740
4.14k
        aPt2=rTrack0[nSiz];
741
4.14k
    }
742
0
    else
743
0
    {
744
0
        const tools::Rectangle& rRectangle = getOutRectangle();
745
0
        if (!rRectangle.IsEmpty()) {
746
0
            aPt1 = rRectangle.TopLeft();
747
0
            aPt2 = rRectangle.BottomRight();
748
0
        }
749
0
    }
750
751
    // #i54102# To allow interactive preview, do also if not inserted
752
4.14k
    const bool bCon1(nullptr != rCon1.m_pSdrObj && rCon1.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
753
4.14k
    const bool bCon2(nullptr != rCon2.m_pSdrObj && rCon2.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
754
4.14k
    const SfxItemSet& rSet = GetObjectItemSet();
755
756
4.14k
    if (bCon1)
757
55
    {
758
55
        if (rCon1.m_pSdrObj==static_cast<SdrObject const *>(this))
759
0
        {
760
            // check, just in case
761
0
            aBoundRect1 = getOutRectangle();
762
0
        }
763
55
        else
764
55
        {
765
55
            if (getSdrModelFromSdrObject().GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect))
766
0
                aBoundRect1 = rCon1.m_pSdrObj->GetSnapRect();
767
55
            else
768
55
                aBoundRect1 = rCon1.m_pSdrObj->GetCurrentBoundRect();
769
55
        }
770
771
55
        aBoundRect1.Move(rCon1.m_aObjOfs.X(),rCon1.m_aObjOfs.Y());
772
55
        aBewareRect1=aBoundRect1;
773
55
        sal_Int32 nH = rSet.Get(SDRATTR_EDGENODE1HORZDIST).GetValue();
774
55
        sal_Int32 nV = rSet.Get(SDRATTR_EDGENODE1VERTDIST).GetValue();
775
55
        aBewareRect1.AdjustLeft( -nH );
776
55
        aBewareRect1.AdjustRight(nH );
777
55
        aBewareRect1.AdjustTop( -nV );
778
55
        aBewareRect1.AdjustBottom(nV );
779
55
    }
780
4.08k
    else
781
4.08k
    {
782
4.08k
        aBoundRect1=tools::Rectangle(aPt1,aPt1);
783
4.08k
        aBoundRect1.Move(rCon1.m_aObjOfs.X(),rCon1.m_aObjOfs.Y());
784
4.08k
        aBewareRect1=aBoundRect1;
785
4.08k
    }
786
787
4.14k
    if (bCon2)
788
55
    {
789
55
        if (rCon2.m_pSdrObj==static_cast<SdrObject const *>(this))
790
0
        { // check, just in case
791
0
            aBoundRect2 = getOutRectangle();
792
0
        }
793
55
        else
794
55
        {
795
55
            if (getSdrModelFromSdrObject().GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect))
796
0
                aBoundRect2 = rCon2.m_pSdrObj->GetSnapRect();
797
55
            else
798
55
                aBoundRect2 = rCon2.m_pSdrObj->GetCurrentBoundRect();
799
55
        }
800
801
55
        aBoundRect2.Move(rCon2.m_aObjOfs.X(),rCon2.m_aObjOfs.Y());
802
55
        aBewareRect2=aBoundRect2;
803
55
        sal_Int32 nH = rSet.Get(SDRATTR_EDGENODE2HORZDIST).GetValue();
804
55
        sal_Int32 nV = rSet.Get(SDRATTR_EDGENODE2VERTDIST).GetValue();
805
55
        aBewareRect2.AdjustLeft( -nH );
806
55
        aBewareRect2.AdjustRight(nH );
807
55
        aBewareRect2.AdjustTop( -nV );
808
55
        aBewareRect2.AdjustBottom(nV );
809
55
    }
810
4.08k
    else
811
4.08k
    {
812
4.08k
        aBoundRect2=tools::Rectangle(aPt2,aPt2);
813
4.08k
        aBoundRect2.Move(rCon2.m_aObjOfs.X(),rCon2.m_aObjOfs.Y());
814
4.08k
        aBewareRect2=aBoundRect2;
815
4.08k
    }
816
817
4.14k
    XPolygon aBestXP;
818
4.14k
    sal_uInt64 nBestQual=0xFFFFFFFF;
819
4.14k
    SdrEdgeInfoRec aBestInfo;
820
4.14k
    bool bAuto1=bCon1 && rCon1.m_bBestVertex;
821
4.14k
    bool bAuto2=bCon2 && rCon2.m_bBestVertex;
822
4.14k
    if (bAuto1) rCon1.m_bAutoVertex=true;
823
4.14k
    if (bAuto2) rCon2.m_bAutoVertex=true;
824
4.14k
    sal_uInt16 nBestAuto1=0;
825
4.14k
    sal_uInt16 nBestAuto2=0;
826
4.14k
    sal_uInt16 nCount1=bAuto1 ? 4 : 1;
827
4.14k
    sal_uInt16 nCount2=bAuto2 ? 4 : 1;
828
829
8.34k
    for (sal_uInt16 nNum1=0; nNum1<nCount1; nNum1++)
830
4.20k
    {
831
4.20k
        if (bAuto1) rCon1.m_nConId=nNum1;
832
4.20k
        if (bCon1 && rCon1.TakeGluePoint(aGP1))
833
121
        {
834
121
            aPt1=aGP1.GetPos();
835
121
            nEsc1=aGP1.GetEscDir();
836
121
            if (nEsc1==SdrEscapeDirection::SMART) nEsc1=ImpCalcEscAngle(rCon1.m_pSdrObj,aPt1-rCon1.m_aObjOfs);
837
121
        }
838
8.44k
        for (sal_uInt16 nNum2=0; nNum2<nCount2; nNum2++)
839
4.24k
        {
840
4.24k
            if (bAuto2) rCon2.m_nConId=nNum2;
841
4.24k
            if (bCon2 && rCon2.TakeGluePoint(aGP2))
842
154
            {
843
154
                aPt2=aGP2.GetPos();
844
154
                nEsc2=aGP2.GetEscDir();
845
154
                if (nEsc2==SdrEscapeDirection::SMART) nEsc2=ImpCalcEscAngle(rCon2.m_pSdrObj,aPt2-rCon2.m_aObjOfs);
846
154
            }
847
21.2k
            for (tools::Long nA1=0; nA1<36000; nA1+=9000)
848
16.9k
            {
849
16.9k
                SdrEscapeDirection nE1 = nA1==0 ? SdrEscapeDirection::RIGHT : nA1==9000 ? SdrEscapeDirection::TOP : nA1==18000 ? SdrEscapeDirection::LEFT : nA1==27000 ? SdrEscapeDirection::BOTTOM : SdrEscapeDirection::SMART;
850
84.8k
                for (tools::Long nA2=0; nA2<36000; nA2+=9000)
851
67.8k
                {
852
67.8k
                    SdrEscapeDirection nE2 = nA2==0 ? SdrEscapeDirection::RIGHT : nA2==9000 ? SdrEscapeDirection::TOP : nA2==18000 ? SdrEscapeDirection::LEFT : nA2==27000 ? SdrEscapeDirection::BOTTOM : SdrEscapeDirection::SMART;
853
67.8k
                    if ((nEsc1&nE1) && (nEsc2&nE2))
854
65.5k
                    {
855
65.5k
                        sal_uInt64 nQual=0;
856
65.5k
                        SdrEdgeInfoRec aInfo;
857
65.5k
                        if (pInfo!=nullptr) aInfo=*pInfo;
858
65.5k
                        XPolygon aXP(ImpCalcEdgeTrack(aPt1,nA1,aBoundRect1,aBewareRect1,aPt2,nA2,aBoundRect2,aBewareRect2,&nQual,&aInfo));
859
65.5k
                        if (nQual<nBestQual)
860
6.56k
                        {
861
6.56k
                            aBestXP=std::move(aXP);
862
6.56k
                            nBestQual=nQual;
863
6.56k
                            aBestInfo=aInfo;
864
6.56k
                            nBestAuto1=nNum1;
865
6.56k
                            nBestAuto2=nNum2;
866
6.56k
                        }
867
65.5k
                    }
868
67.8k
                }
869
16.9k
            }
870
4.24k
        }
871
4.20k
    }
872
4.14k
    if (bAuto1) rCon1.m_nConId=nBestAuto1;
873
4.14k
    if (bAuto2) rCon2.m_nConId=nBestAuto2;
874
4.14k
    if (pInfo!=nullptr) *pInfo=aBestInfo;
875
4.14k
    return aBestXP;
876
4.14k
}
877
878
XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const Point& rPt1, tools::Long nAngle1, const tools::Rectangle& rBoundRect1, const tools::Rectangle& rBewareRect1,
879
    const Point& rPt2, tools::Long nAngle2, const tools::Rectangle& rBoundRect2, const tools::Rectangle& rBewareRect2,
880
    sal_uInt64* pnQuality, SdrEdgeInfoRec* pInfo) const
881
65.5k
{
882
65.5k
    SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
883
65.5k
    bool bRts1=nAngle1==0;
884
65.5k
    bool bObn1=nAngle1==9000;
885
65.5k
    bool bLks1=nAngle1==18000;
886
65.5k
    bool bUnt1=nAngle1==27000;
887
65.5k
    bool bHor1=bLks1 || bRts1;
888
65.5k
    bool bVer1=bObn1 || bUnt1;
889
65.5k
    bool bRts2=nAngle2==0;
890
65.5k
    bool bObn2=nAngle2==9000;
891
65.5k
    bool bLks2=nAngle2==18000;
892
65.5k
    bool bUnt2=nAngle2==27000;
893
65.5k
    bool bHor2=bLks2 || bRts2;
894
65.5k
    bool bVer2=bObn2 || bUnt2;
895
65.5k
    if (pInfo) {
896
65.5k
        pInfo->m_nAngle1=nAngle1;
897
65.5k
        pInfo->m_nAngle2=nAngle2;
898
65.5k
        pInfo->m_nObj1Lines=1;
899
65.5k
        pInfo->m_nObj2Lines=1;
900
65.5k
        pInfo->m_nMiddleLine=0xFFFF;
901
65.5k
    }
902
65.5k
    Point aPt1(rPt1);
903
65.5k
    Point aPt2(rPt2);
904
65.5k
    tools::Rectangle aBoundRect1 (rBoundRect1 );
905
65.5k
    tools::Rectangle aBoundRect2 (rBoundRect2 );
906
65.5k
    tools::Rectangle aBewareRect1(rBewareRect1);
907
65.5k
    tools::Rectangle aBewareRect2(rBewareRect2);
908
65.5k
    Point aMeeting((aPt1.X()+aPt2.X()+1)/2,(aPt1.Y()+aPt2.Y()+1)/2);
909
65.5k
    if (eKind==SdrEdgeKind::OneLine) {
910
26.6k
        XPolygon aXP(2);
911
26.6k
        aXP[0]=rPt1;
912
26.6k
        aXP[1]=rPt2;
913
26.6k
        if (pnQuality!=nullptr) {
914
26.6k
            *pnQuality=std::abs(rPt1.X()-rPt2.X())+std::abs(rPt1.Y()-rPt2.Y());
915
26.6k
        }
916
26.6k
        return aXP;
917
38.8k
    } else if (eKind==SdrEdgeKind::ThreeLines) {
918
0
        XPolygon aXP(4);
919
0
        aXP[0]=rPt1;
920
0
        aXP[1]=rPt1;
921
0
        aXP[2]=rPt2;
922
0
        aXP[3]=rPt2;
923
0
        if (bRts1) aXP[1].setX(aBewareRect1.Right() );  //+=500;
924
0
        if (bObn1) aXP[1].setY(aBewareRect1.Top() );    //-=500;
925
0
        if (bLks1) aXP[1].setX(aBewareRect1.Left() );   //-=500;
926
0
        if (bUnt1) aXP[1].setY(aBewareRect1.Bottom() ); //+=500;
927
0
        if (bRts2) aXP[2].setX(aBewareRect2.Right() );  //+=500;
928
0
        if (bObn2) aXP[2].setY(aBewareRect2.Top() );    //-=500;
929
0
        if (bLks2) aXP[2].setX(aBewareRect2.Left() );   //-=500;
930
0
        if (bUnt2) aXP[2].setY(aBewareRect2.Bottom() ); //+=500;
931
0
        if (pnQuality!=nullptr) {
932
0
            tools::Long nQ=std::abs(aXP[1].X()-aXP[0].X())+std::abs(aXP[1].Y()-aXP[0].Y());
933
0
            nQ+=std::abs(aXP[2].X()-aXP[1].X())+std::abs(aXP[2].Y()-aXP[1].Y());
934
0
            nQ+=std::abs(aXP[3].X()-aXP[2].X())+std::abs(aXP[3].Y()-aXP[2].Y());
935
0
            *pnQuality=nQ;
936
0
        }
937
0
        if (pInfo) {
938
0
            pInfo->m_nObj1Lines=2;
939
0
            pInfo->m_nObj2Lines=2;
940
0
            if (bHor1) {
941
0
                aXP[1].AdjustX(pInfo->m_aObj1Line2.X() );
942
0
            } else {
943
0
                aXP[1].AdjustY(pInfo->m_aObj1Line2.Y() );
944
0
            }
945
0
            if (bHor2) {
946
0
                aXP[2].AdjustX(pInfo->m_aObj2Line2.X() );
947
0
            } else {
948
0
                aXP[2].AdjustY(pInfo->m_aObj2Line2.Y() );
949
0
            }
950
0
        }
951
0
        return aXP;
952
0
    }
953
38.8k
    sal_uInt16 nIntersections=0;
954
38.8k
    {
955
38.8k
        Point aC1(aBewareRect1.Center());
956
38.8k
        Point aC2(aBewareRect2.Center());
957
38.8k
        if (aBewareRect1.Left()<=aBewareRect2.Right() && aBewareRect1.Right()>=aBewareRect2.Left()) {
958
            // overlapping on the x axis
959
22.4k
            tools::Long n1=std::max(aBewareRect1.Left(),aBewareRect2.Left());
960
22.4k
            tools::Long n2=std::min(aBewareRect1.Right(),aBewareRect2.Right());
961
22.4k
            aMeeting.setX((n1+n2+1)/2 );
962
22.4k
        } else {
963
            // otherwise the center point of the empty space
964
16.4k
            if (aC1.X()<aC2.X()) {
965
6.97k
                aMeeting.setX((aBewareRect1.Right()+aBewareRect2.Left()+1)/2 );
966
9.47k
            } else {
967
9.47k
                aMeeting.setX((aBewareRect1.Left()+aBewareRect2.Right()+1)/2 );
968
9.47k
            }
969
16.4k
        }
970
38.8k
        if (aBewareRect1.Top()<=aBewareRect2.Bottom() && aBewareRect1.Bottom()>=aBewareRect2.Top()) {
971
            // overlapping on the x axis
972
19.8k
            tools::Long n1=std::max(aBewareRect1.Top(),aBewareRect2.Top());
973
19.8k
            tools::Long n2=std::min(aBewareRect1.Bottom(),aBewareRect2.Bottom());
974
19.8k
            aMeeting.setY((n1+n2+1)/2 );
975
19.8k
        } else {
976
            // otherwise the center point of the empty space
977
19.0k
            if (aC1.Y()<aC2.Y()) {
978
16.8k
                aMeeting.setY((aBewareRect1.Bottom()+aBewareRect2.Top()+1)/2 );
979
16.8k
            } else {
980
2.14k
                aMeeting.setY((aBewareRect1.Top()+aBewareRect2.Bottom()+1)/2 );
981
2.14k
            }
982
19.0k
        }
983
        // Here, there are three cases:
984
        //   1. both go into the same direction
985
        //   2. both go into opposite directions
986
        //   3. one is vertical, the other is horizontal
987
38.8k
        tools::Long nXMin=std::min(aBewareRect1.Left(),aBewareRect2.Left());
988
38.8k
        tools::Long nXMax=std::max(aBewareRect1.Right(),aBewareRect2.Right());
989
38.8k
        tools::Long nYMin=std::min(aBewareRect1.Top(),aBewareRect2.Top());
990
38.8k
        tools::Long nYMax=std::max(aBewareRect1.Bottom(),aBewareRect2.Bottom());
991
38.8k
        bool bBewareOverlap=aBewareRect1.Right()>aBewareRect2.Left() && aBewareRect1.Left()<aBewareRect2.Right() &&
992
0
                            aBewareRect1.Bottom()>aBewareRect2.Top() && aBewareRect1.Top()<aBewareRect2.Bottom();
993
38.8k
        unsigned nMainCase=3;
994
38.8k
        if (nAngle1==nAngle2) nMainCase=1;
995
29.1k
        else if ((bHor1 && bHor2) || (bVer1 && bVer2)) nMainCase=2;
996
38.8k
        if (nMainCase==1) { // case 1 (both go in one direction) is possible
997
9.71k
            if (bVer1) aMeeting.setX((aPt1.X()+aPt2.X()+1)/2 ); // Here, this is better than
998
9.71k
            if (bHor1) aMeeting.setY((aPt1.Y()+aPt2.Y()+1)/2 ); // using center point of empty space
999
            // bX1Ok means that the vertical exiting Obj1 doesn't conflict with Obj2, ...
1000
9.71k
            bool bX1Ok=aPt1.X()<=aBewareRect2.Left() || aPt1.X()>=aBewareRect2.Right();
1001
9.71k
            bool bX2Ok=aPt2.X()<=aBewareRect1.Left() || aPt2.X()>=aBewareRect1.Right();
1002
9.71k
            bool bY1Ok=aPt1.Y()<=aBewareRect2.Top() || aPt1.Y()>=aBewareRect2.Bottom();
1003
9.71k
            bool bY2Ok=aPt2.Y()<=aBewareRect1.Top() || aPt2.Y()>=aBewareRect1.Bottom();
1004
9.71k
            if (bLks1 && (bY1Ok || aBewareRect1.Left()<aBewareRect2.Right()) && (bY2Ok || aBewareRect2.Left()<aBewareRect1.Right())) {
1005
2.42k
                aMeeting.setX(nXMin );
1006
2.42k
            }
1007
9.71k
            if (bRts1 && (bY1Ok || aBewareRect1.Right()>aBewareRect2.Left()) && (bY2Ok || aBewareRect2.Right()>aBewareRect1.Left())) {
1008
2.42k
                aMeeting.setX(nXMax );
1009
2.42k
            }
1010
9.71k
            if (bObn1 && (bX1Ok || aBewareRect1.Top()<aBewareRect2.Bottom()) && (bX2Ok || aBewareRect2.Top()<aBewareRect1.Bottom())) {
1011
2.42k
                aMeeting.setY(nYMin );
1012
2.42k
            }
1013
9.71k
            if (bUnt1 && (bX1Ok || aBewareRect1.Bottom()>aBewareRect2.Top()) && (bX2Ok || aBewareRect2.Bottom()>aBewareRect1.Top())) {
1014
2.42k
                aMeeting.setY(nYMax );
1015
2.42k
            }
1016
29.1k
        } else if (nMainCase==2) {
1017
            // case 2:
1018
9.71k
            if (bHor1) { // both horizontal
1019
                /* 9 sub-cases:
1020
               (legend: line exits to the left (-|), right (|-))
1021
1022
                    2.1: Facing; overlap only on y axis
1023
                         *  *  *
1024
                         |--|  *
1025
                         *  *  *
1026
1027
                    2.2, 2.3: Facing, offset vertically; no overlap on either
1028
                             axis
1029
                         |- *  *       *  *  *
1030
                         * -|  *       * -|  *
1031
                         *  *  *  ,    *  *  *
1032
1033
                    2.4, 2.5: One below the other; overlap only on y axis
1034
                         *  |- *       *  *  *
1035
                         * -|  *       * -|  *
1036
                         *  *  *  ,    *  |- *
1037
1038
                    2.6, 2.7: Not facing, offset vertically; no overlap on either
1039
                             axis
1040
                         *  *  |-      *  *  *
1041
                         * -|  *       * -|  *
1042
                         *  *  *  ,    *  *  |-
1043
1044
                    2.8: Not facing; overlap only on y axis
1045
                         *  *  *
1046
                         * -|  |-
1047
                         *  *  *
1048
1049
                    2.9: The objects's BewareRects overlap on x and y axis
1050
1051
                   These cases, with some modifications are also valid for
1052
                   horizontal line exits.
1053
                   Cases 2.1 through 2.7 are covered well enough with the
1054
                   default meetings. Only for cases 2.8 and 2.9 do we determine
1055
                   special meeting points here.
1056
                */
1057
1058
                // normalization; be aR1 the one exiting to the right,
1059
                // be aR2 the one exiting to the left
1060
4.85k
                tools::Rectangle aBewR1(bRts1 ? aBewareRect1 : aBewareRect2);
1061
4.85k
                tools::Rectangle aBewR2(bRts1 ? aBewareRect2 : aBewareRect1);
1062
4.85k
                tools::Rectangle aBndR1(bRts1 ? aBoundRect1 : aBoundRect2);
1063
4.85k
                tools::Rectangle aBndR2(bRts1 ? aBoundRect2 : aBoundRect1);
1064
4.85k
                if (aBewR1.Bottom()>aBewR2.Top() && aBewR1.Top()<aBewR2.Bottom()) {
1065
                    // overlap on y axis; cases 2.1, 2.8, 2.9
1066
0
                    if (aBewR1.Right()>aBewR2.Left()) {
1067
                        /* Cases 2.8, 2.9:
1068
                             Case 2.8: always going around on the outside
1069
                             (bDirect=false).
1070
1071
                             Case 2.9 could also be a direct connection (in the
1072
                             case that the BewareRects overlap only slightly and
1073
                             the BoundRects don't overlap at all and if the
1074
                             line exits would otherwise violate the respective
1075
                             other object's BewareRect).
1076
                        */
1077
0
                        bool bCase29Direct = false;
1078
0
                        bool bCase29=aBewR1.Right()>aBewR2.Left();
1079
0
                        if (aBndR1.Right()<=aBndR2.Left()) { // case 2.9 without BoundRect overlap
1080
0
                            if ((aPt1.Y()>aBewareRect2.Top() && aPt1.Y()<aBewareRect2.Bottom()) ||
1081
0
                                (aPt2.Y()>aBewareRect1.Top() && aPt2.Y()<aBewareRect1.Bottom())) {
1082
0
                               bCase29Direct = true;
1083
0
                            }
1084
0
                        }
1085
0
                        if (!bCase29Direct) {
1086
0
                            bool bObenLang=std::abs(nYMin-aMeeting.Y())<=std::abs(nYMax-aMeeting.Y());
1087
0
                            if (bObenLang) {
1088
0
                                aMeeting.setY(nYMin );
1089
0
                            } else {
1090
0
                                aMeeting.setY(nYMax );
1091
0
                            }
1092
0
                            if (bCase29) {
1093
                                // now make sure that the surrounded object
1094
                                // isn't traversed
1095
0
                                if ((aBewR1.Center().Y()<aBewR2.Center().Y()) != bObenLang) {
1096
0
                                    aMeeting.setX(aBewR2.Right() );
1097
0
                                } else {
1098
0
                                    aMeeting.setX(aBewR1.Left() );
1099
0
                                }
1100
0
                            }
1101
0
                        } else {
1102
                            // We need a direct connection (3-line Z connection),
1103
                            // because we have to violate the BewareRects.
1104
                            // Use rule of three to scale down the BewareRects.
1105
0
                            tools::Long nWant1=aBewR1.Right()-aBndR1.Right(); // distance at Obj1
1106
0
                            tools::Long nWant2=aBndR2.Left()-aBewR2.Left();   // distance at Obj2
1107
0
                            tools::Long nSpace=aBndR2.Left()-aBndR1.Right(); // available space
1108
0
                            tools::Long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
1109
0
                            tools::Long nGet2=nSpace-nGet1;
1110
0
                            if (bRts1) { // revert normalization
1111
0
                                aBewareRect1.AdjustRight(nGet1-nWant1 );
1112
0
                                aBewareRect2.AdjustLeft( -(nGet2-nWant2) );
1113
0
                            } else {
1114
0
                                aBewareRect2.AdjustRight(nGet1-nWant1 );
1115
0
                                aBewareRect1.AdjustLeft( -(nGet2-nWant2) );
1116
0
                            }
1117
0
                            nIntersections++; // lower quality
1118
0
                        }
1119
0
                    }
1120
0
                }
1121
4.85k
            } else if (bVer1) { // both horizontal
1122
4.85k
                tools::Rectangle aBewR1(bUnt1 ? aBewareRect1 : aBewareRect2);
1123
4.85k
                tools::Rectangle aBewR2(bUnt1 ? aBewareRect2 : aBewareRect1);
1124
4.85k
                tools::Rectangle aBndR1(bUnt1 ? aBoundRect1 : aBoundRect2);
1125
4.85k
                tools::Rectangle aBndR2(bUnt1 ? aBoundRect2 : aBoundRect1);
1126
4.85k
                if (aBewR1.Right()>aBewR2.Left() && aBewR1.Left()<aBewR2.Right()) {
1127
                    // overlap on y axis; cases 2.1, 2.8, 2.9
1128
0
                    if (aBewR1.Bottom()>aBewR2.Top()) {
1129
                        /* Cases 2.8, 2.9
1130
                           Case 2.8 always going around on the outside (bDirect=false).
1131
1132
                           Case 2.9 could also be a direct connection (in the
1133
                           case that the BewareRects overlap only slightly and
1134
                           the BoundRects don't overlap at all and if the
1135
                           line exits would otherwise violate the respective
1136
                           other object's BewareRect).
1137
                        */
1138
0
                        bool bCase29Direct = false;
1139
0
                        bool bCase29=aBewR1.Bottom()>aBewR2.Top();
1140
0
                        if (aBndR1.Bottom()<=aBndR2.Top()) { // case 2.9 without BoundRect overlap
1141
0
                            if ((aPt1.X()>aBewareRect2.Left() && aPt1.X()<aBewareRect2.Right()) ||
1142
0
                                (aPt2.X()>aBewareRect1.Left() && aPt2.X()<aBewareRect1.Right())) {
1143
0
                               bCase29Direct = true;
1144
0
                            }
1145
0
                        }
1146
0
                        if (!bCase29Direct) {
1147
0
                            bool bLinksLang=std::abs(nXMin-aMeeting.X())<=std::abs(nXMax-aMeeting.X());
1148
0
                            if (bLinksLang) {
1149
0
                                aMeeting.setX(nXMin );
1150
0
                            } else {
1151
0
                                aMeeting.setX(nXMax );
1152
0
                            }
1153
0
                            if (bCase29) {
1154
                                // now make sure that the surrounded object
1155
                                // isn't traversed
1156
0
                                if ((aBewR1.Center().X()<aBewR2.Center().X()) != bLinksLang) {
1157
0
                                    aMeeting.setY(aBewR2.Bottom() );
1158
0
                                } else {
1159
0
                                    aMeeting.setY(aBewR1.Top() );
1160
0
                                }
1161
0
                            }
1162
0
                        } else {
1163
                            // We need a direct connection (3-line Z connection),
1164
                            // because we have to violate the BewareRects.
1165
                            // Use rule of three to scale down the BewareRects.
1166
0
                            tools::Long nWant1=aBewR1.Bottom()-aBndR1.Bottom(); // difference at Obj1
1167
0
                            tools::Long nWant2=aBndR2.Top()-aBewR2.Top();   // difference at Obj2
1168
0
                            tools::Long nSpace=aBndR2.Top()-aBndR1.Bottom(); // available space
1169
0
                            tools::Long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
1170
0
                            tools::Long nGet2=nSpace-nGet1;
1171
0
                            if (bUnt1) { // revert normalization
1172
0
                                aBewareRect1.AdjustBottom(nGet1-nWant1 );
1173
0
                                aBewareRect2.AdjustTop( -(nGet2-nWant2) );
1174
0
                            } else {
1175
0
                                aBewareRect2.AdjustBottom(nGet1-nWant1 );
1176
0
                                aBewareRect1.AdjustTop( -(nGet2-nWant2) );
1177
0
                            }
1178
0
                            nIntersections++; // lower quality
1179
0
                        }
1180
0
                    }
1181
0
                }
1182
4.85k
            }
1183
19.4k
        } else if (nMainCase==3) { // case 3: one horizontal, the other vertical
1184
            /* legend:
1185
               The line exits to the:
1186
               -|       left
1187
1188
                |-      right
1189
1190
               _|_      top
1191
1192
                T       bottom
1193
1194
               *  .  *  .  * -- no overlap, at most might touch
1195
               .  .  .  .  . -- overlap
1196
               *  .  |- .  * -- same height
1197
               .  .  .  .  . -- overlap
1198
               *  .  *  .  * -- no overlap, at most might touch
1199
1200
               Overall, there are 96 possible constellations, some of these can't even
1201
               be unambiguously assigned to a certain case/method of handling.
1202
1203
1204
               3.1: All those constellations that are covered reasonably well
1205
               by the default MeetingPoint (20+12).
1206
1207
               T  T  T  . _|_    _|_ .  T  T  T     these 12     *  .  *  T  *      *  .  *  .  *      *  T  *  .  *      *  .  *  .  *
1208
               .  .  .  . _|_    _|_ .  .  .  .  constellations  .  .  .  .  .      .  .  .  .  T      .  .  .  .  .      T  .  .  .  .
1209
               *  .  |- .  *      *  . -|  .  *   are covered    *  .  |- . _|_     *  .  |- .  T     _|_ . -|  .  *      T  . -|  .  *
1210
               .  .  .  .  T      T  .  .  .  .     only in      .  .  .  . _|_     .  .  .  .  .     _|_ .  .  .  .      .  .  .  .  .
1211
              _|__|__|_ .  T      T  . _|__|__|_     part:       *  .  * _|_ *      *  .  *  .  *      * _|_ *  .  *      *  .  *  .  *
1212
1213
              The last 16 of these cases can be excluded, if the objects face each other openly.
1214
1215
1216
              3.2: The objects face each other openly, thus a connection using only two lines is possible (4+20);
1217
              This case is priority #1.
1218
               *  .  *  .  T      T  .  *  .  *     these 20     *  .  *  T  *      *  T  *  .  *      *  .  *  .  *      *  .  *  .  *
1219
               .  .  .  .  .      .  .  .  .  .  constellations  .  .  .  T  T      T  T  .  .  .      .  .  .  .  .      .  .  .  .  .
1220
               *  .  |- .  *      *  . -|  .  *    are covered   *  .  |-_|__|_    _|__|_-|  .  *      *  .  |- T  T      T  T -|  .  *
1221
               .  .  .  .  .      .  .  .  .  .     only in      .  .  . _|__|_    _|__|_ .  .  .      .  .  .  .  .      .  .  .  .  .
1222
               *  .  *  . _|_    _|_ .  *  .  *      part:       *  .  * _|_ *      * _|_ *  .  *      *  .  *  .  *      *  .  *  .  *
1223
1224
               3.3: The line exits point away from the other object or miss its back (52+4).
1225
              _|__|__|__|_ *      * _|__|__|__|_     *  .  .  .  *      *  .  *  .  *     these 4      *  .  *  .  *      *  .  *  .  *
1226
              _|__|__|__|_ .      . _|__|__|__|_     T  T  T  .  .      .  .  T  T  T  constellations  .  .  .  T  .      .  T  .  .  .
1227
              _|__|_ |- .  *      *  . -| _|__|_     T  T  |- .  *      *  . -|  T  T    are covered   *  .  |- .  *      *  . -|  .  *
1228
              _|__|__|_ .  .      .  . _|__|__|_     T  T  T  T  .      .  T  T  T  T     only in      .  .  . _|_ .      . _|_ .  .  .
1229
               *  .  *  .  *      *  .  *  .  *      T  T  T  T  *      *  T  T  T  T      part:       *  .  *  .  *      *  .  *  .  *
1230
            */
1231
1232
            // case 3.2
1233
19.4k
            tools::Rectangle aTmpR1(aBewareRect1);
1234
19.4k
            tools::Rectangle aTmpR2(aBewareRect2);
1235
19.4k
            if (bBewareOverlap) {
1236
                // overlapping BewareRects: use BoundRects for checking for case 3.2
1237
0
                aTmpR1=aBoundRect1;
1238
0
                aTmpR2=aBoundRect2;
1239
0
            }
1240
19.4k
            if ((((bRts1 && aTmpR1.Right ()<=aPt2.X()) || (bLks1 && aTmpR1.Left()>=aPt2.X())) &&
1241
7.65k
                 ((bUnt2 && aTmpR2.Bottom()<=aPt1.Y()) || (bObn2 && aTmpR2.Top ()>=aPt1.Y()))) ||
1242
13.1k
                (((bRts2 && aTmpR2.Right ()<=aPt1.X()) || (bLks2 && aTmpR2.Left()>=aPt1.X())) &&
1243
12.4k
                 ((bUnt1 && aTmpR1.Bottom()<=aPt2.Y()) || (bObn1 && aTmpR1.Top ()>=aPt2.Y())))) {
1244
                // case 3.2 applies: connector with only 2 lines
1245
12.4k
                if (bHor1) {
1246
6.22k
                    aMeeting.setX(aPt2.X() );
1247
6.22k
                    aMeeting.setY(aPt1.Y() );
1248
6.22k
                } else {
1249
6.22k
                    aMeeting.setX(aPt1.X() );
1250
6.22k
                    aMeeting.setY(aPt2.Y() );
1251
6.22k
                }
1252
                // in the case of overlapping BewareRects:
1253
12.4k
                aBewareRect1=aTmpR1;
1254
12.4k
                aBewareRect2=aTmpR2;
1255
12.4k
            } else if ((((bRts1 && aBewareRect1.Right ()>aBewareRect2.Left  ()) ||
1256
5.78k
                         (bLks1 && aBewareRect1.Left  ()<aBewareRect2.Right ())) &&
1257
2.05k
                        ((bUnt2 && aBewareRect2.Bottom()>aBewareRect1.Top   ()) ||
1258
1.24k
                         (bObn2 && aBewareRect2.Top   ()<aBewareRect1.Bottom()))) ||
1259
6.02k
                       (((bRts2 && aBewareRect2.Right ()>aBewareRect1.Left  ()) ||
1260
5.14k
                         (bLks2 && aBewareRect2.Left  ()<aBewareRect1.Right ())) &&
1261
2.05k
                        ((bUnt1 && aBewareRect1.Bottom()>aBewareRect2.Top   ()) ||
1262
1.92k
                         (bObn1 && aBewareRect1.Top   ()<aBewareRect2.Bottom())))) {
1263
                // case 3.3
1264
1.89k
                if (bRts1 || bRts2) { aMeeting.setX(nXMax ); }
1265
1.89k
                if (bLks1 || bLks2) { aMeeting.setX(nXMin ); }
1266
1.89k
                if (bUnt1 || bUnt2) { aMeeting.setY(nYMax ); }
1267
1.89k
                if (bObn1 || bObn2) { aMeeting.setY(nYMin ); }
1268
1.89k
            }
1269
19.4k
        }
1270
38.8k
    }
1271
1272
38.8k
    XPolygon aXP1(ImpCalcObjToCenter(aPt1,nAngle1,aBewareRect1,aMeeting));
1273
38.8k
    XPolygon aXP2(ImpCalcObjToCenter(aPt2,nAngle2,aBewareRect2,aMeeting));
1274
38.8k
    sal_uInt16 nXP1Cnt=aXP1.GetPointCount();
1275
38.8k
    sal_uInt16 nXP2Cnt=aXP2.GetPointCount();
1276
38.8k
    assert(nXP1Cnt >= 2 && nXP2Cnt >= 2 && "ImpCalcObjToCenter inserts a min of 2 points");
1277
38.8k
    if (pInfo) {
1278
38.8k
        pInfo->m_nObj1Lines=nXP1Cnt; pInfo->m_nObj1Lines--;
1279
38.8k
        pInfo->m_nObj2Lines=nXP2Cnt; pInfo->m_nObj2Lines--;
1280
38.8k
    }
1281
38.8k
    Point aEP1(aXP1[nXP1Cnt-1]);
1282
38.8k
    Point aEP2(aXP2[nXP2Cnt-1]);
1283
38.8k
    bool bInsMeetingPoint=aEP1.X()!=aEP2.X() && aEP1.Y()!=aEP2.Y();
1284
38.8k
    bool bHorzE1=aEP1.Y()==aXP1[nXP1Cnt-2].Y(); // is last line of XP1 horizontal?
1285
38.8k
    bool bHorzE2=aEP2.Y()==aXP2[nXP2Cnt-2].Y(); // is last line of XP2 horizontal?
1286
38.8k
    if (aEP1==aEP2 && ((bHorzE1 && bHorzE2 && aEP1.Y()==aEP2.Y()) || (!bHorzE1 && !bHorzE2 && aEP1.X()==aEP2.X()))) {
1287
        // special casing 'I' connectors
1288
19.3k
        nXP1Cnt--; aXP1.Remove(nXP1Cnt,1);
1289
19.3k
        nXP2Cnt--; aXP2.Remove(nXP2Cnt,1);
1290
19.3k
    }
1291
38.8k
    if (bInsMeetingPoint) {
1292
1.89k
        aXP1.Insert(XPOLY_APPEND,aMeeting,PolyFlags::Normal);
1293
1.89k
        if (pInfo) {
1294
            // Inserting a MeetingPoint adds 2 new lines,
1295
            // either might become the center line.
1296
1.89k
            if (pInfo->m_nObj1Lines==pInfo->m_nObj2Lines) {
1297
1.89k
                pInfo->m_nObj1Lines++;
1298
1.89k
                pInfo->m_nObj2Lines++;
1299
1.89k
            } else {
1300
0
                if (pInfo->m_nObj1Lines>pInfo->m_nObj2Lines) {
1301
0
                    pInfo->m_nObj2Lines++;
1302
0
                    pInfo->m_nMiddleLine=nXP1Cnt-1;
1303
0
                } else {
1304
0
                    pInfo->m_nObj1Lines++;
1305
0
                    pInfo->m_nMiddleLine=nXP1Cnt;
1306
0
                }
1307
0
            }
1308
1.89k
        }
1309
36.9k
    } else if (pInfo && aEP1!=aEP2 && nXP1Cnt+nXP2Cnt>=4) {
1310
        // By connecting both ends, another line is added, this becomes the center line.
1311
14.2k
        pInfo->m_nMiddleLine=nXP1Cnt-1;
1312
14.2k
    }
1313
38.8k
    sal_uInt16 nNum=aXP2.GetPointCount();
1314
38.8k
    if (nXP1Cnt > 1 && nXP2Cnt > 1 && aXP1[nXP1Cnt-1] == aXP2[nXP2Cnt-1]) nNum--;
1315
98.6k
    while (nNum>0) {
1316
59.7k
        nNum--;
1317
59.7k
        aXP1.Insert(XPOLY_APPEND,aXP2[nNum],PolyFlags::Normal);
1318
59.7k
    }
1319
38.8k
    sal_uInt16 nPointCount=aXP1.GetPointCount();
1320
38.8k
    char cForm;
1321
38.8k
    if (pInfo || pnQuality!=nullptr) {
1322
38.8k
        if (nPointCount==2) cForm='I';
1323
19.4k
        else if (nPointCount==3) cForm='L';
1324
16.1k
        else if (nPointCount==4) { // Z or U
1325
6.96k
            if (nAngle1==nAngle2) cForm='U';
1326
2.53k
            else cForm='Z';
1327
9.18k
        } else if (nPointCount==6) { // S or C or ...
1328
2.21k
            if (nAngle1!=nAngle2) {
1329
                // For type S, line 2 has the same direction as line 4.
1330
                // For type C, the opposite is true.
1331
2.21k
                Point aP1(aXP1[1]);
1332
2.21k
                Point aP2(aXP1[2]);
1333
2.21k
                Point aP3(aXP1[3]);
1334
2.21k
                Point aP4(aXP1[4]);
1335
2.21k
                if (aP1.Y()==aP2.Y()) { // else both lines are horizontal
1336
1.26k
                    if ((aP1.X()<aP2.X())==(aP3.X()<aP4.X())) cForm='S';
1337
0
                    else cForm='C';
1338
1.26k
                } else { // else both lines are vertical
1339
948
                    if ((aP1.Y()<aP2.Y())==(aP3.Y()<aP4.Y())) cForm='S';
1340
0
                    else cForm='C';
1341
948
                }
1342
2.21k
            } else cForm='4'; // else is case 3 with 5 lines
1343
6.96k
        } else cForm='?';
1344
        // more shapes:
1345
38.8k
        if (pInfo) {
1346
38.8k
            if (cForm=='I' || cForm=='L' || cForm=='Z' || cForm=='U') {
1347
29.6k
                pInfo->m_nObj1Lines=1;
1348
29.6k
                pInfo->m_nObj2Lines=1;
1349
29.6k
                if (cForm=='Z' || cForm=='U') {
1350
6.96k
                    pInfo->m_nMiddleLine=1;
1351
22.6k
                } else {
1352
22.6k
                    pInfo->m_nMiddleLine=0xFFFF;
1353
22.6k
                }
1354
29.6k
            } else if (cForm=='S' || cForm=='C') {
1355
2.21k
                pInfo->m_nObj1Lines=2;
1356
2.21k
                pInfo->m_nObj2Lines=2;
1357
2.21k
                pInfo->m_nMiddleLine=2;
1358
2.21k
            }
1359
38.8k
        }
1360
38.8k
    }
1361
0
    else
1362
0
    {
1363
0
        cForm = 0;
1364
0
    }
1365
38.8k
    if (pnQuality!=nullptr) {
1366
38.8k
        sal_uInt64 nQual=0;
1367
38.8k
        sal_uInt64 nQual0=nQual; // prevent overruns
1368
38.8k
        bool bOverflow = false;
1369
38.8k
        Point aPt0(aXP1[0]);
1370
124k
        for (sal_uInt16 nPntNum=1; nPntNum<nPointCount; nPntNum++) {
1371
85.8k
            Point aPt1b(aXP1[nPntNum]);
1372
85.8k
            nQual+=std::abs(aPt1b.X()-aPt0.X())+std::abs(aPt1b.Y()-aPt0.Y());
1373
85.8k
            if (nQual<nQual0) bOverflow = true;
1374
85.8k
            nQual0=nQual;
1375
85.8k
            aPt0=aPt1b;
1376
85.8k
        }
1377
1378
38.8k
        sal_uInt16 nTmp=nPointCount;
1379
38.8k
        if (cForm=='Z') {
1380
2.53k
            nTmp=2; // Z shape with good quality (nTmp=2 instead of 4)
1381
2.53k
            sal_uInt64 n1=std::abs(aXP1[1].X()-aXP1[0].X())+std::abs(aXP1[1].Y()-aXP1[0].Y());
1382
2.53k
            sal_uInt64 n2=std::abs(aXP1[2].X()-aXP1[1].X())+std::abs(aXP1[2].Y()-aXP1[1].Y());
1383
2.53k
            sal_uInt64 n3=std::abs(aXP1[3].X()-aXP1[2].X())+std::abs(aXP1[3].Y()-aXP1[2].Y());
1384
            // try to make lines lengths similar
1385
2.53k
            sal_uInt64 nBesser=0;
1386
2.53k
            n1+=n3;
1387
2.53k
            n3=n2/4;
1388
2.53k
            if (n1>=n2) nBesser=6;
1389
1.58k
            else if (n1>=3*n3) nBesser=4;
1390
1.29k
            else if (n1>=2*n3) nBesser=2;
1391
2.53k
            if (aXP1[0].Y()!=aXP1[1].Y()) nBesser++; // vertical starting line gets a plus (for H/V-Prio)
1392
2.53k
            if (nQual>nBesser) nQual-=nBesser; else nQual=0;
1393
2.53k
        }
1394
38.8k
        if (nTmp>=3) {
1395
16.9k
            nQual0=nQual;
1396
16.9k
            nQual+=static_cast<sal_uInt64>(nTmp)*0x01000000;
1397
16.9k
            if (nQual<nQual0 || nTmp>15) bOverflow = true;
1398
16.9k
        }
1399
38.8k
        if (nPointCount>=2) { // check exit angle again
1400
38.8k
            Point aP1(aXP1[1]); aP1-=aXP1[0];
1401
38.8k
            Point aP2(aXP1[nPointCount-2]); aP2-=aXP1[nPointCount-1];
1402
38.8k
            tools::Long nAng1=0; if (aP1.X()<0) nAng1=18000; if (aP1.Y()>0) nAng1=27000;
1403
38.8k
            if (aP1.Y()<0) nAng1=9000;
1404
38.8k
            if (aP1.X()!=0 && aP1.Y()!=0) nAng1=1; // slant?!
1405
38.8k
            tools::Long nAng2=0; if (aP2.X()<0) nAng2=18000; if (aP2.Y()>0) nAng2=27000;
1406
38.8k
            if (aP2.Y()<0) nAng2=9000;
1407
38.8k
            if (aP2.X()!=0 && aP2.Y()!=0) nAng2=1; // slant?!
1408
38.8k
            if (nAng1!=nAngle1) nIntersections++;
1409
38.8k
            if (nAng2!=nAngle2) nIntersections++;
1410
38.8k
        }
1411
1412
        // For the quality check, use the original Rects and at the same time
1413
        // check whether one them was scaled down for the calculation of the
1414
        // Edges (e. g. case 2.9)
1415
38.8k
        aBewareRect1=rBewareRect1;
1416
38.8k
        aBewareRect2=rBewareRect2;
1417
1418
163k
        for (sal_uInt16 i=0; i<nPointCount; i++) {
1419
124k
            Point aPt1b(aXP1[i]);
1420
124k
            bool b1=aPt1b.X()>aBewareRect1.Left() && aPt1b.X()<aBewareRect1.Right() &&
1421
0
                        aPt1b.Y()>aBewareRect1.Top() && aPt1b.Y()<aBewareRect1.Bottom();
1422
124k
            bool b2=aPt1b.X()>aBewareRect2.Left() && aPt1b.X()<aBewareRect2.Right() &&
1423
0
                        aPt1b.Y()>aBewareRect2.Top() && aPt1b.Y()<aBewareRect2.Bottom();
1424
124k
            sal_uInt16 nInt0=nIntersections;
1425
124k
            if (i==0 || i==nPointCount-1) {
1426
77.6k
                if (b1 && b2) nIntersections++;
1427
77.6k
            } else {
1428
47.0k
                if (b1) nIntersections++;
1429
47.0k
                if (b2) nIntersections++;
1430
47.0k
            }
1431
            // check for overlaps
1432
124k
            if (i>0 && nInt0==nIntersections) {
1433
85.8k
                if (aPt0.Y()==aPt1b.Y()) { // horizontal line
1434
63.0k
                    if (aPt0.Y()>aBewareRect1.Top() && aPt0.Y()<aBewareRect1.Bottom() &&
1435
0
                        ((aPt0.X()<=aBewareRect1.Left() && aPt1b.X()>=aBewareRect1.Right()) ||
1436
0
                         (aPt1b.X()<=aBewareRect1.Left() && aPt0.X()>=aBewareRect1.Right()))) nIntersections++;
1437
63.0k
                    if (aPt0.Y()>aBewareRect2.Top() && aPt0.Y()<aBewareRect2.Bottom() &&
1438
0
                        ((aPt0.X()<=aBewareRect2.Left() && aPt1b.X()>=aBewareRect2.Right()) ||
1439
0
                         (aPt1b.X()<=aBewareRect2.Left() && aPt0.X()>=aBewareRect2.Right()))) nIntersections++;
1440
63.0k
                } else { // vertical line
1441
22.8k
                    if (aPt0.X()>aBewareRect1.Left() && aPt0.X()<aBewareRect1.Right() &&
1442
0
                        ((aPt0.Y()<=aBewareRect1.Top() && aPt1b.Y()>=aBewareRect1.Bottom()) ||
1443
0
                         (aPt1b.Y()<=aBewareRect1.Top() && aPt0.Y()>=aBewareRect1.Bottom()))) nIntersections++;
1444
22.8k
                    if (aPt0.X()>aBewareRect2.Left() && aPt0.X()<aBewareRect2.Right() &&
1445
0
                        ((aPt0.Y()<=aBewareRect2.Top() && aPt1b.Y()>=aBewareRect2.Bottom()) ||
1446
0
                         (aPt1b.Y()<=aBewareRect2.Top() && aPt0.Y()>=aBewareRect2.Bottom()))) nIntersections++;
1447
22.8k
                }
1448
85.8k
            }
1449
124k
            aPt0=aPt1b;
1450
124k
        }
1451
38.8k
        if (nPointCount<=1) nIntersections++;
1452
38.8k
        nQual0=nQual;
1453
38.8k
        nQual+=static_cast<sal_uInt64>(nIntersections)*0x10000000;
1454
38.8k
        if (nQual<nQual0 || nIntersections>15) bOverflow = true;
1455
1456
38.8k
        if (bOverflow || nQual==0xFFFFFFFF) nQual=0xFFFFFFFE;
1457
38.8k
        *pnQuality=nQual;
1458
38.8k
    }
1459
38.8k
    if (pInfo) { // now apply line offsets to aXP1
1460
38.8k
        if (pInfo->m_nMiddleLine!=0xFFFF) {
1461
14.2k
            sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::MiddleLine,aXP1);
1462
14.2k
            if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::MiddleLine,aXP1)) {
1463
6.40k
                aXP1[nIdx].AdjustY(pInfo->m_aMiddleLine.Y() );
1464
6.40k
                aXP1[nIdx+1].AdjustY(pInfo->m_aMiddleLine.Y() );
1465
7.84k
            } else {
1466
7.84k
                aXP1[nIdx].AdjustX(pInfo->m_aMiddleLine.X() );
1467
7.84k
                aXP1[nIdx+1].AdjustX(pInfo->m_aMiddleLine.X() );
1468
7.84k
            }
1469
14.2k
        }
1470
38.8k
        if (pInfo->m_nObj1Lines>=2) {
1471
6.64k
            sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line2,aXP1);
1472
6.64k
            if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line2,aXP1)) {
1473
3.56k
                aXP1[nIdx].AdjustY(pInfo->m_aObj1Line2.Y() );
1474
3.56k
                aXP1[nIdx+1].AdjustY(pInfo->m_aObj1Line2.Y() );
1475
3.56k
            } else {
1476
3.08k
                aXP1[nIdx].AdjustX(pInfo->m_aObj1Line2.X() );
1477
3.08k
                aXP1[nIdx+1].AdjustX(pInfo->m_aObj1Line2.X() );
1478
3.08k
            }
1479
6.64k
        }
1480
38.8k
        if (pInfo->m_nObj1Lines>=3) {
1481
0
            sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line3,aXP1);
1482
0
            if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line3,aXP1)) {
1483
0
                aXP1[nIdx].AdjustY(pInfo->m_aObj1Line3.Y() );
1484
0
                aXP1[nIdx+1].AdjustY(pInfo->m_aObj1Line3.Y() );
1485
0
            } else {
1486
0
                aXP1[nIdx].AdjustX(pInfo->m_aObj1Line3.X() );
1487
0
                aXP1[nIdx+1].AdjustX(pInfo->m_aObj1Line3.X() );
1488
0
            }
1489
0
        }
1490
38.8k
        if (pInfo->m_nObj2Lines>=2) {
1491
6.64k
            sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line2,aXP1);
1492
6.64k
            if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line2,aXP1)) {
1493
3.56k
                aXP1[nIdx].AdjustY(pInfo->m_aObj2Line2.Y() );
1494
3.56k
                aXP1[nIdx+1].AdjustY(pInfo->m_aObj2Line2.Y() );
1495
3.56k
            } else {
1496
3.08k
                aXP1[nIdx].AdjustX(pInfo->m_aObj2Line2.X() );
1497
3.08k
                aXP1[nIdx+1].AdjustX(pInfo->m_aObj2Line2.X() );
1498
3.08k
            }
1499
6.64k
        }
1500
38.8k
        if (pInfo->m_nObj2Lines>=3) {
1501
0
            sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line3,aXP1);
1502
0
            if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line3,aXP1)) {
1503
0
                aXP1[nIdx].AdjustY(pInfo->m_aObj2Line3.Y() );
1504
0
                aXP1[nIdx+1].AdjustY(pInfo->m_aObj2Line3.Y() );
1505
0
            } else {
1506
0
                aXP1[nIdx].AdjustX(pInfo->m_aObj2Line3.X() );
1507
0
                aXP1[nIdx+1].AdjustX(pInfo->m_aObj2Line3.X() );
1508
0
            }
1509
0
        }
1510
38.8k
    }
1511
    // make the connector a bezier curve, if appropriate
1512
38.8k
    if (eKind != SdrEdgeKind::Bezier || nPointCount <= 2)
1513
38.8k
        return aXP1;
1514
1515
0
    if (pInfo && pInfo->m_bUseOOXMLCurve) // Routing method OOXML
1516
0
    {
1517
        // The additional points needed are located on the segments of the path of the
1518
        // corresponding bentConnector as calculated above.
1519
0
        auto SegmentPoint = [&aXP1](const sal_uInt16& nEnd, const double& fFactor) {
1520
0
            return Point(
1521
0
                aXP1[nEnd - 1].X() + basegfx::fround<tools::Long>(fFactor * (aXP1[nEnd].X() - aXP1[nEnd - 1].X())),
1522
0
                aXP1[nEnd - 1].Y() + basegfx::fround<tools::Long>(fFactor * (aXP1[nEnd].Y() - aXP1[nEnd - 1].Y())));
1523
0
        };
1524
1525
        // We change the path going from end to start. Thus inserting points does not affect the index
1526
        // of the preceding points.
1527
        // The end point has index nPointCount-1 and is a normal point and kept.
1528
        // Insert new control point in the middle of last segments.
1529
0
        Point aControl = SegmentPoint(nPointCount - 1, 0.5);
1530
        // Insert happens before specified index.
1531
0
        aXP1.Insert(nPointCount - 1, aControl, PolyFlags::Control);
1532
0
        for (sal_uInt16 nSegment = nPointCount - 2; nSegment > 1; --nSegment)
1533
0
        {
1534
            // We need a normal point at center of segment and control points at 1/4 and 3/4 of
1535
            // segment. At center and 1/4 are new points, at 3/4 will be replacement for the end
1536
            // point of the segment.
1537
0
            aControl = SegmentPoint(nSegment, 0.25);
1538
0
            Point aNormal = SegmentPoint(nSegment, 0.5);
1539
0
            aXP1.SetFlags(nSegment, PolyFlags::Control);
1540
0
            aXP1[nSegment] = SegmentPoint(nSegment, 0.75);
1541
0
            aXP1.Insert(nSegment, aNormal, PolyFlags::Normal);
1542
0
            aXP1.Insert(nSegment, aControl, PolyFlags::Control);
1543
0
        }
1544
        // The first segments needs a control point in the middle. It is replacement for the
1545
        // second point.
1546
0
        aXP1.SetFlags(1, PolyFlags::Control);
1547
0
        aXP1[1] = SegmentPoint(1, 0.5);
1548
0
    }
1549
0
    else // Routing method LO
1550
0
    {
1551
0
        Point* pPt1=&aXP1[0];
1552
0
        Point* pPt2=&aXP1[1];
1553
0
        Point* pPt3=&aXP1[nPointCount-2];
1554
0
        Point* pPt4=&aXP1[nPointCount-1];
1555
0
        tools::Long dx1=pPt2->X()-pPt1->X();
1556
0
        tools::Long dy1=pPt2->Y()-pPt1->Y();
1557
0
        tools::Long dx2=pPt3->X()-pPt4->X();
1558
0
        tools::Long dy2=pPt3->Y()-pPt4->Y();
1559
0
        if (cForm=='L') { // nPointCount==3
1560
0
            aXP1.SetFlags(1,PolyFlags::Control);
1561
0
            Point aPt3(*pPt2);
1562
0
            aXP1.Insert(2,aPt3,PolyFlags::Control);
1563
0
            nPointCount=aXP1.GetPointCount();
1564
0
            pPt2=&aXP1[1];
1565
0
            pPt3=&aXP1[nPointCount-2];
1566
0
            pPt2->AdjustX( -(dx1/3) );
1567
0
            pPt2->AdjustY( -(dy1/3) );
1568
0
            pPt3->AdjustX( -(dx2/3) );
1569
0
            pPt3->AdjustY( -(dy2/3) );
1570
0
        } else if (nPointCount>=4 && nPointCount<=6) { // Z or U or ...
1571
            // To all others, the end points of the original lines become control
1572
            // points for now. Thus, we need to do some more work for nPointCount>4!
1573
0
            aXP1.SetFlags(1,PolyFlags::Control);
1574
0
            aXP1.SetFlags(nPointCount-2,PolyFlags::Control);
1575
            // distance x1.5
1576
0
            pPt2->AdjustX(dx1/2 );
1577
0
            pPt2->AdjustY(dy1/2 );
1578
0
            pPt3->AdjustX(dx2/2 );
1579
0
            pPt3->AdjustY(dy2/2 );
1580
0
            if (nPointCount==5) {
1581
                // add a control point before and after center
1582
0
                Point aCenter(aXP1[2]);
1583
0
                tools::Long dx1b=aCenter.X()-aXP1[1].X();
1584
0
                tools::Long dy1b=aCenter.Y()-aXP1[1].Y();
1585
0
                tools::Long dx2b=aCenter.X()-aXP1[3].X();
1586
0
                tools::Long dy2b=aCenter.Y()-aXP1[3].Y();
1587
0
                aXP1.Insert(2,aCenter,PolyFlags::Control);
1588
0
                aXP1.SetFlags(3,PolyFlags::Symmetric);
1589
0
                aXP1.Insert(4,aCenter,PolyFlags::Control);
1590
0
                aXP1[2].AdjustX( -(dx1b/2) );
1591
0
                aXP1[2].AdjustY( -(dy1b/2) );
1592
0
                aXP1[3].AdjustX( -((dx1b+dx2b)/4) );
1593
0
                aXP1[3].AdjustY( -((dy1b+dy2b)/4) );
1594
0
                aXP1[4].AdjustX( -(dx2b/2) );
1595
0
                aXP1[4].AdjustY( -(dy2b/2) );
1596
0
            }
1597
0
            if (nPointCount==6) {
1598
0
                Point aPt1b(aXP1[2]);
1599
0
                Point aPt2b(aXP1[3]);
1600
0
                aXP1.Insert(2,aPt1b,PolyFlags::Control);
1601
0
                aXP1.Insert(5,aPt2b,PolyFlags::Control);
1602
0
                tools::Long dx=aPt1b.X()-aPt2b.X();
1603
0
                tools::Long dy=aPt1b.Y()-aPt2b.Y();
1604
0
                aXP1[3].AdjustX( -(dx/2) );
1605
0
                aXP1[3].AdjustY( -(dy/2) );
1606
0
                aXP1.SetFlags(3,PolyFlags::Symmetric);
1607
0
                aXP1.Remove(4,1); // because it's identical with aXP1[3]
1608
0
            }
1609
0
        }
1610
0
    }
1611
0
    return aXP1;
1612
38.8k
}
1613
1614
/*
1615
There could be a maximum of 64 different developments with 5 lines, a
1616
maximum of 32 developments with 4 lines, a maximum of 16 developments with
1617
3 lines, a maximum of 8 developments with 2 lines.
1618
This gives us a total of 124 possibilities.
1619
Normalized for the 1st exit angle to the right, there remain 31 possibilities.
1620
Now, normalizing away the vertical mirroring, we get to a total of 16
1621
characteristic developments with 1 through 5 lines:
1622
1623
1 line  (type "I")  --
1624
1625
2 lines (type "L")  __|
1626
1627
3 lines (type "U")  __          (type "Z")      _
1628
                    __|                       _|
1629
                                     _                              _
1630
4 lines          #1   _|        #2  | |         #3 |_          #4  | |
1631
                    _|             _|               _|              _|
1632
    Of these, #1 is implausible, #2 is a rotated version of #3. This leaves
1633
    #2 (from now on referred to as 4.1) and #4 (from now on referred to as 4.2).
1634
                      _                _
1635
5 lines          #1   _|        #2   _|         #3   ___       #4   _
1636
                    _|             _|              _|  _|         _| |_
1637
                    _               _                _
1638
                 #5  |_         #6 |_           #7 _| |        #8  ____
1639
                      _|            _|               _|           |_  _|
1640
    Of these, 5.1, 5.2, 5.4 and 5.5 are implausible, 5.7 is a reversed version
1641
    of 5.3. This leaves 5.3 (type "4"), 5.6 (type "S") and 5.8 (type "C").
1642
1643
We now have discerned the 9 basic types to cover all 400 possible constellations
1644
of object positions and exit angles. 4 of the 9 types have got a center
1645
line (CL). The number of object margins per object varies between 0 and 3:
1646
1647
        CL      O1      O2      Note
1648
"I":    n       0       0
1649
"L":    n       0       0
1650
"U":    n       0-1     0-1
1651
"Z":    y       0       0
1652
4.2:    y       0       1       = U+1, respectively 1+U
1653
4.4:    n       0-2     0-2     = Z+1
1654
"4":    y       0       2       = Z+2
1655
"S":    y       1       1       = 1+Z+1
1656
"C":    n       0-3     0-3     = 1+U+1
1657
*/
1658
1659
void SdrEdgeObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
1660
8.16k
{
1661
8.16k
    const SfxHintId nId = rHint.GetId();
1662
8.16k
    bool bDataChg=nId==SfxHintId::DataChanged;
1663
8.16k
    bool bDying=nId==SfxHintId::Dying;
1664
8.16k
    bool bObj1=m_aCon1.m_pSdrObj!=nullptr && m_aCon1.m_pSdrObj->GetBroadcaster()==&rBC;
1665
8.16k
    bool bObj2=m_aCon2.m_pSdrObj!=nullptr && m_aCon2.m_pSdrObj->GetBroadcaster()==&rBC;
1666
8.16k
    if (bDying && (bObj1 || bObj2)) {
1667
        // catch Dying, so AttrObj doesn't start broadcasting
1668
        // about an alleged change of template
1669
1.74k
        if (bObj1) m_aCon1.m_pSdrObj=nullptr;
1670
1.74k
        if (bObj2) m_aCon2.m_pSdrObj=nullptr;
1671
1.74k
        return;
1672
1.74k
    }
1673
6.41k
    if ( bObj1 || bObj2 )
1674
6.39k
    {
1675
6.39k
        m_bEdgeTrackUserDefined = false;
1676
6.39k
    }
1677
6.41k
    SdrTextObj::Notify(rBC,rHint);
1678
6.41k
    if (m_nNotifyingCount!=0)return;
1679
1680
// a locking flag
1681
6.41k
    m_nNotifyingCount++;
1682
6.41k
    const SdrHint* pSdrHint = ( rHint.GetId() == SfxHintId::ThisIsAnSdrHint ? static_cast<const SdrHint*>(&rHint) : nullptr );
1683
1684
6.41k
    if (bDataChg) { // StyleSheet changed
1685
6.39k
        ImpSetAttrToEdgeInfo(); // when changing templates, copy values from Pool to aEdgeInfo
1686
6.39k
    }
1687
6.41k
    if (bDataChg                                ||
1688
18
        (bObj1 && m_aCon1.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
1689
18
        (bObj2 && m_aCon2.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
1690
18
        (pSdrHint && pSdrHint->GetKind()==SdrHintKind::ObjectRemoved))
1691
6.39k
    {
1692
        // broadcasting only, if on the same page
1693
6.39k
        tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
1694
6.39k
        ImpDirtyEdgeTrack();
1695
1696
        // only redraw here, object hasn't actually changed
1697
6.39k
        ActionChanged();
1698
1699
6.39k
        SendUserCall(SdrUserCallType::Resize,aBoundRect0);
1700
6.39k
    }
1701
6.41k
    m_nNotifyingCount--;
1702
6.41k
}
1703
1704
/** updates edges that are connected to the edges of this object
1705
    as if the connected objects sent a repaint broadcast
1706
*/
1707
void SdrEdgeObj::Reformat()
1708
9.94k
{
1709
9.94k
    if( nullptr != m_aCon1.m_pSdrObj )
1710
2.99k
    {
1711
2.99k
        SfxHint aHint( SfxHintId::DataChanged );
1712
2.99k
        Notify( *m_aCon1.m_pSdrObj->GetBroadcaster(), aHint );
1713
2.99k
    }
1714
1715
9.94k
    if( nullptr != m_aCon2.m_pSdrObj )
1716
3.40k
    {
1717
3.40k
        SfxHint aHint( SfxHintId::DataChanged );
1718
3.40k
        Notify( *m_aCon2.m_pSdrObj->GetBroadcaster(), aHint );
1719
3.40k
    }
1720
9.94k
}
1721
1722
rtl::Reference<SdrObject> SdrEdgeObj::CloneSdrObject(SdrModel& rTargetModel) const
1723
19
{
1724
19
    return new SdrEdgeObj(rTargetModel, *this);
1725
19
}
1726
1727
OUString SdrEdgeObj::TakeObjNameSingul() const
1728
0
{
1729
0
    OUString sName(SvxResId(STR_ObjNameSingulEDGE));
1730
1731
0
    OUString aName(GetName());
1732
0
    if (!aName.isEmpty())
1733
0
        sName += " '" + aName + "'";
1734
0
    return sName;
1735
0
}
1736
1737
OUString SdrEdgeObj::TakeObjNamePlural() const
1738
0
{
1739
0
    return SvxResId(STR_ObjNamePluralEDGE);
1740
0
}
1741
1742
basegfx::B2DPolyPolygon SdrEdgeObj::TakeXorPoly() const
1743
0
{
1744
0
    basegfx::B2DPolyPolygon aPolyPolygon;
1745
1746
0
    if (m_bEdgeTrackDirty)
1747
0
    {
1748
0
        const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
1749
0
    }
1750
1751
0
    if(m_pEdgeTrack)
1752
0
    {
1753
0
        aPolyPolygon.append(m_pEdgeTrack->getB2DPolygon());
1754
0
    }
1755
1756
0
    return aPolyPolygon;
1757
0
}
1758
1759
void SdrEdgeObj::SetEdgeTrackPath( const basegfx::B2DPolyPolygon& rPoly )
1760
9.31k
{
1761
9.31k
    if ( !rPoly.count() )
1762
85
    {
1763
85
        m_bEdgeTrackDirty = true;
1764
85
        m_bEdgeTrackUserDefined = false;
1765
85
    }
1766
9.22k
    else
1767
9.22k
    {
1768
9.22k
        *m_pEdgeTrack = XPolygon( rPoly.getB2DPolygon( 0 ) );
1769
9.22k
        m_bEdgeTrackDirty = false;
1770
9.22k
        m_bEdgeTrackUserDefined = true;
1771
1772
        // #i110629# also set aRect and maSnapeRect depending on pEdgeTrack
1773
9.22k
        const tools::Rectangle aPolygonBounds(m_pEdgeTrack->GetBoundRect());
1774
9.22k
        setRectangle(aPolygonBounds);
1775
9.22k
        maSnapRect = aPolygonBounds;
1776
9.22k
    }
1777
9.31k
}
1778
1779
basegfx::B2DPolyPolygon SdrEdgeObj::GetEdgeTrackPath() const
1780
0
{
1781
0
    basegfx::B2DPolyPolygon aPolyPolygon;
1782
1783
0
    if (m_bEdgeTrackDirty)
1784
0
        const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
1785
1786
0
    aPolyPolygon.append( m_pEdgeTrack->getB2DPolygon() );
1787
1788
0
    return aPolyPolygon;
1789
0
}
1790
1791
sal_uInt32 SdrEdgeObj::GetHdlCount() const
1792
0
{
1793
0
    SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
1794
0
    sal_uInt32 nHdlCnt(0);
1795
0
    sal_uInt32 nPointCount(m_pEdgeTrack->GetPointCount());
1796
1797
0
    if(nPointCount)
1798
0
    {
1799
0
        nHdlCnt = 2;
1800
0
        if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
1801
0
        {
1802
0
             sal_uInt32 nO1(m_aEdgeInfo.m_nObj1Lines > 0 ? m_aEdgeInfo.m_nObj1Lines - 1 : 0);
1803
0
             sal_uInt32 nO2(m_aEdgeInfo.m_nObj2Lines > 0 ? m_aEdgeInfo.m_nObj2Lines - 1 : 0);
1804
0
             sal_uInt32 nM(m_aEdgeInfo.m_nMiddleLine != 0xFFFF ? 1 : 0);
1805
0
             nHdlCnt += nO1 + nO2 + nM;
1806
0
        }
1807
0
        else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
1808
0
        {
1809
0
            if(GetConnectedNode(true))
1810
0
                nHdlCnt++;
1811
1812
0
            if(GetConnectedNode(false))
1813
0
                nHdlCnt++;
1814
0
        }
1815
0
    }
1816
1817
0
    return nHdlCnt;
1818
0
}
1819
1820
void SdrEdgeObj::AddToHdlList(SdrHdlList& rHdlList) const
1821
0
{
1822
0
    sal_uInt32 nPointCount(m_pEdgeTrack->GetPointCount());
1823
0
    if (nPointCount==0)
1824
0
        return;
1825
1826
0
    {
1827
0
        std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*m_pEdgeTrack)[0],SdrHdlKind::Poly));
1828
0
        if (m_aCon1.m_pSdrObj!=nullptr && m_aCon1.m_bBestVertex) pHdl->Set1PixMore();
1829
0
        pHdl->SetPointNum(0);
1830
0
        rHdlList.AddHdl(std::move(pHdl));
1831
0
    }
1832
0
    {
1833
0
        std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*m_pEdgeTrack)[sal_uInt16(nPointCount-1)],SdrHdlKind::Poly));
1834
0
        if (m_aCon2.m_pSdrObj!=nullptr && m_aCon2.m_bBestVertex) pHdl->Set1PixMore();
1835
0
        pHdl->SetPointNum(1);
1836
0
        rHdlList.AddHdl(std::move(pHdl));
1837
0
    }
1838
0
    {
1839
0
        SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
1840
0
        if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
1841
0
        {
1842
0
            sal_uInt32 nO1(m_aEdgeInfo.m_nObj1Lines > 0 ? m_aEdgeInfo.m_nObj1Lines - 1 : 0);
1843
0
            sal_uInt32 nO2(m_aEdgeInfo.m_nObj2Lines > 0 ? m_aEdgeInfo.m_nObj2Lines - 1 : 0);
1844
0
            sal_uInt32 nM(m_aEdgeInfo.m_nMiddleLine != 0xFFFF ? 1 : 0);
1845
0
            bool bOOXMLCurve = m_aEdgeInfo.m_bUseOOXMLCurve && eKind == SdrEdgeKind::Bezier;
1846
0
            for(sal_uInt32 i = 0; i < (nO1 + nO2 + nM); ++i)
1847
0
            {
1848
0
                sal_Int32 nPt(0);
1849
0
                sal_uInt32 nNum = i;
1850
0
                std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(Point(),SdrHdlKind::Poly));
1851
0
                if (nNum<nO1)
1852
0
                {
1853
0
                    nPt = bOOXMLCurve ? (nNum + 1) * 3 : nNum + 1;
1854
0
                    if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
1855
0
                    if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line3);
1856
0
                } else {
1857
0
                    nNum=nNum-nO1;
1858
0
                    if (nNum<nO2)
1859
0
                    {
1860
0
                        nPt = bOOXMLCurve ? nPointCount - 4 - nNum * 3 : nPointCount - 3 - nNum;
1861
0
                        if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
1862
0
                        if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line3);
1863
0
                    } else {
1864
0
                        nNum=nNum-nO2;
1865
0
                        if (nNum<nM) {
1866
0
                            nPt = bOOXMLCurve ? m_aEdgeInfo.m_nMiddleLine * 3
1867
0
                                              : m_aEdgeInfo.m_nMiddleLine;
1868
0
                            pHdl->SetLineCode(SdrEdgeLineCode::MiddleLine);
1869
0
                        }
1870
0
                    }
1871
0
                }
1872
0
                if (nPt>0)
1873
0
                {
1874
0
                    if (bOOXMLCurve)
1875
0
                    {
1876
0
                        Point aPos((*m_pEdgeTrack)[static_cast<sal_uInt16>(nPt)]);
1877
0
                        pHdl->SetPos(aPos);
1878
0
                    }
1879
0
                    else
1880
0
                    {
1881
0
                        Point aPos((*m_pEdgeTrack)[static_cast<sal_uInt16>(nPt)]);
1882
0
                        aPos+=(*m_pEdgeTrack)[static_cast<sal_uInt16>(nPt)+1];
1883
0
                        aPos.setX( aPos.X() / 2 );
1884
0
                        aPos.setY( aPos.Y() / 2 );
1885
0
                        pHdl->SetPos(aPos);
1886
0
                    }
1887
0
                    pHdl->SetPointNum(i + 2);
1888
0
                    rHdlList.AddHdl(std::move(pHdl));
1889
0
                }
1890
0
            }
1891
0
        }
1892
0
        else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
1893
0
        {
1894
0
            if(GetConnectedNode(true))
1895
0
            {
1896
0
                Point aPos((*m_pEdgeTrack)[1]);
1897
0
                std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
1898
0
                pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
1899
0
                pHdl->SetPointNum(2);
1900
0
                rHdlList.AddHdl(std::move(pHdl));
1901
0
            }
1902
0
            if(GetConnectedNode(false))
1903
0
            {
1904
0
                Point aPos((*m_pEdgeTrack)[2]);
1905
0
                std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
1906
0
                pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
1907
0
                pHdl->SetPointNum(3);
1908
0
                rHdlList.AddHdl(std::move(pHdl));
1909
0
            }
1910
0
        }
1911
0
    }
1912
0
}
1913
1914
bool SdrEdgeObj::hasSpecialDrag() const
1915
0
{
1916
0
    return true;
1917
0
}
1918
1919
rtl::Reference<SdrObject> SdrEdgeObj::getFullDragClone() const
1920
0
{
1921
    // use Clone operator
1922
0
    rtl::Reference<SdrEdgeObj> pRetval = SdrObject::Clone(*this, getSdrModelFromSdrObject());
1923
1924
    // copy connections for clone, SdrEdgeObj::operator= does not do this
1925
0
    pRetval->ConnectToNode(true, GetConnectedNode(true));
1926
0
    pRetval->ConnectToNode(false, GetConnectedNode(false));
1927
1928
0
    return pRetval;
1929
0
}
1930
1931
bool SdrEdgeObj::beginSpecialDrag(SdrDragStat& rDrag) const
1932
0
{
1933
0
    if(!rDrag.GetHdl())
1934
0
        return false;
1935
1936
0
    rDrag.SetEndDragChangesAttributes(true);
1937
1938
0
    if(rDrag.GetHdl()->GetPointNum() < 2)
1939
0
    {
1940
0
        rDrag.SetNoSnap();
1941
0
    }
1942
1943
0
    return true;
1944
0
}
1945
1946
bool SdrEdgeObj::applySpecialDrag(SdrDragStat& rDragStat)
1947
0
{
1948
0
    SdrEdgeObj* pOriginalEdge = dynamic_cast< SdrEdgeObj* >(rDragStat.GetHdl()->GetObj());
1949
0
    const bool bOriginalEdgeModified(pOriginalEdge == this);
1950
1951
0
    if(!bOriginalEdgeModified && pOriginalEdge)
1952
0
    {
1953
        // copy connections when clone is modified. This is needed because
1954
        // as preparation to this modification the data from the original object
1955
        // was copied to the clone using the operator=. As can be seen there,
1956
        // that operator does not copy the connections (for good reason)
1957
0
        ConnectToNode(true, pOriginalEdge->GetConnection(true).GetSdrObject());
1958
0
        ConnectToNode(false, pOriginalEdge->GetConnection(false).GetSdrObject());
1959
0
    }
1960
1961
0
    if(rDragStat.GetHdl()->GetPointNum() < 2)
1962
0
    {
1963
        // start or end point connector drag
1964
0
        const bool bDragA(0 == rDragStat.GetHdl()->GetPointNum());
1965
0
        const Point aPointNow(rDragStat.GetNow());
1966
1967
0
        rDragStat.SetEndDragChangesGeoAndAttributes(true);
1968
1969
0
        if(rDragStat.GetPageView())
1970
0
        {
1971
0
            SdrObjConnection* pDraggedOne(bDragA ? &m_aCon1 : &m_aCon2);
1972
1973
            // clear connection
1974
0
            DisconnectFromNode(bDragA);
1975
1976
            // look for new connection
1977
0
            ImpFindConnector(aPointNow, *rDragStat.GetPageView(), *pDraggedOne, pOriginalEdge, nullptr, &rDragStat);
1978
1979
0
            if(pDraggedOne->m_pSdrObj)
1980
0
            {
1981
                // if found, officially connect to it; ImpFindConnector only
1982
                // sets pObj hard
1983
0
                SdrObject* pNewConnection = pDraggedOne->m_pSdrObj;
1984
0
                pDraggedOne->m_pSdrObj = nullptr;
1985
0
                ConnectToNode(bDragA, pNewConnection);
1986
0
            }
1987
1988
0
            if(rDragStat.GetView() && !bOriginalEdgeModified)
1989
0
            {
1990
                // show IA helper, but only do this during IA, so not when the original
1991
                // Edge gets modified in the last call
1992
0
                rDragStat.GetView()->SetConnectMarker(*pDraggedOne);
1993
0
            }
1994
0
        }
1995
1996
0
        if(m_pEdgeTrack)
1997
0
        {
1998
            // change pEdgeTrack to modified position
1999
0
            if(bDragA)
2000
0
            {
2001
0
                (*m_pEdgeTrack)[0] = aPointNow;
2002
0
            }
2003
0
            else
2004
0
            {
2005
0
                (*m_pEdgeTrack)[sal_uInt16(m_pEdgeTrack->GetPointCount()-1)] = aPointNow;
2006
0
            }
2007
0
        }
2008
2009
        // reset edge info's offsets, this is an end point drag
2010
0
        m_aEdgeInfo.m_aObj1Line2 = Point();
2011
0
        m_aEdgeInfo.m_aObj1Line3 = Point();
2012
0
        m_aEdgeInfo.m_aObj2Line2 = Point();
2013
0
        m_aEdgeInfo.m_aObj2Line3 = Point();
2014
0
        m_aEdgeInfo.m_aMiddleLine = Point();
2015
0
    }
2016
0
    else
2017
0
    {
2018
        // control point connector drag
2019
0
        const ImpEdgeHdl* pEdgeHdl = static_cast<const ImpEdgeHdl*>(rDragStat.GetHdl());
2020
0
        const SdrEdgeLineCode eLineCode = pEdgeHdl->GetLineCode();
2021
0
        const Point aDist(rDragStat.GetNow() - rDragStat.GetStart());
2022
0
        sal_Int32 nDist(pEdgeHdl->IsHorzDrag() ? aDist.X() : aDist.Y());
2023
2024
0
        nDist += m_aEdgeInfo.ImpGetLineOffset(eLineCode, *m_pEdgeTrack);
2025
0
        m_aEdgeInfo.ImpSetLineOffset(eLineCode, *m_pEdgeTrack, nDist);
2026
0
    }
2027
2028
    // force recalculation of EdgeTrack
2029
0
    *m_pEdgeTrack = ImpCalcEdgeTrack(*m_pEdgeTrack, m_aCon1, m_aCon2, &m_aEdgeInfo);
2030
0
    m_bEdgeTrackDirty=false;
2031
2032
    // save EdgeInfos and mark object as user modified
2033
0
    ImpSetEdgeInfoToAttr();
2034
0
    m_bEdgeTrackUserDefined = false;
2035
2036
0
    SetBoundAndSnapRectsDirty();
2037
2038
0
    if(bOriginalEdgeModified && rDragStat.GetView())
2039
0
    {
2040
        // hide connect marker helper again when original gets changed.
2041
        // This happens at the end of the interaction
2042
0
        rDragStat.GetView()->HideConnectMarker();
2043
0
    }
2044
2045
0
    return true;
2046
0
}
2047
2048
OUString SdrEdgeObj::getSpecialDragComment(const SdrDragStat& rDrag) const
2049
0
{
2050
0
    const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
2051
2052
0
    if(bCreateComment)
2053
0
    {
2054
0
        return OUString();
2055
0
    }
2056
0
    else
2057
0
    {
2058
0
        return ImpGetDescriptionStr(STR_DragEdgeTail);
2059
0
    }
2060
0
}
2061
2062
2063
basegfx::B2DPolygon SdrEdgeObj::ImplAddConnectorOverlay(const SdrDragMethod& rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
2064
0
{
2065
0
    basegfx::B2DPolygon aResult;
2066
2067
0
    if(bDetail)
2068
0
    {
2069
0
        SdrObjConnection aMyCon1(m_aCon1);
2070
0
        SdrObjConnection aMyCon2(m_aCon2);
2071
2072
0
        if (bTail1)
2073
0
        {
2074
0
            const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon1.m_aObjOfs.X(), aMyCon1.m_aObjOfs.Y()));
2075
0
            aMyCon1.m_aObjOfs.setX( basegfx::fround<tools::Long>(aTemp.getX()) );
2076
0
            aMyCon1.m_aObjOfs.setY( basegfx::fround<tools::Long>(aTemp.getY()) );
2077
0
        }
2078
2079
0
        if (bTail2)
2080
0
        {
2081
0
            const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon2.m_aObjOfs.X(), aMyCon2.m_aObjOfs.Y()));
2082
0
            aMyCon2.m_aObjOfs.setX( basegfx::fround<tools::Long>(aTemp.getX()) );
2083
0
            aMyCon2.m_aObjOfs.setY( basegfx::fround<tools::Long>(aTemp.getY()) );
2084
0
        }
2085
2086
0
        SdrEdgeInfoRec aInfo(m_aEdgeInfo);
2087
0
        XPolygon aXP(ImpCalcEdgeTrack(*m_pEdgeTrack, aMyCon1, aMyCon2, &aInfo));
2088
2089
0
        if(aXP.GetPointCount())
2090
0
        {
2091
0
            aResult = aXP.getB2DPolygon();
2092
0
        }
2093
0
    }
2094
0
    else
2095
0
    {
2096
0
        Point aPt1((*m_pEdgeTrack)[0]);
2097
0
        Point aPt2((*m_pEdgeTrack)[sal_uInt16(m_pEdgeTrack->GetPointCount() - 1)]);
2098
2099
0
        if (m_aCon1.m_pSdrObj && (m_aCon1.m_bBestConn || m_aCon1.m_bBestVertex))
2100
0
            aPt1 = m_aCon1.m_pSdrObj->GetSnapRect().Center();
2101
2102
0
        if (m_aCon2.m_pSdrObj && (m_aCon2.m_bBestConn || m_aCon2.m_bBestVertex))
2103
0
            aPt2 = m_aCon2.m_pSdrObj->GetSnapRect().Center();
2104
2105
0
        if (bTail1)
2106
0
        {
2107
0
            const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
2108
0
            aPt1.setX( basegfx::fround<tools::Long>(aTemp.getX()) );
2109
0
            aPt1.setY( basegfx::fround<tools::Long>(aTemp.getY()) );
2110
0
        }
2111
2112
0
        if (bTail2)
2113
0
        {
2114
0
            const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
2115
0
            aPt2.setX( basegfx::fround<tools::Long>(aTemp.getX()) );
2116
0
            aPt2.setY( basegfx::fround<tools::Long>(aTemp.getY()) );
2117
0
        }
2118
2119
0
        aResult.append(basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
2120
0
        aResult.append(basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
2121
0
    }
2122
2123
0
    return aResult;
2124
0
}
2125
2126
bool SdrEdgeObj::BegCreate(SdrDragStat& rDragStat)
2127
0
{
2128
0
    rDragStat.SetNoSnap();
2129
0
    m_pEdgeTrack->SetPointCount(2);
2130
0
    (*m_pEdgeTrack)[0]=rDragStat.GetStart();
2131
0
    (*m_pEdgeTrack)[1]=rDragStat.GetNow();
2132
0
    if (rDragStat.GetPageView()!=nullptr) {
2133
0
        ImpFindConnector(rDragStat.GetStart(),*rDragStat.GetPageView(),m_aCon1,this);
2134
0
        ConnectToNode(true,m_aCon1.m_pSdrObj);
2135
0
    }
2136
0
    *m_pEdgeTrack=ImpCalcEdgeTrack(*m_pEdgeTrack,m_aCon1,m_aCon2,&m_aEdgeInfo);
2137
0
    return true;
2138
0
}
2139
2140
bool SdrEdgeObj::MovCreate(SdrDragStat& rDragStat)
2141
0
{
2142
0
    sal_uInt16 nMax=m_pEdgeTrack->GetPointCount();
2143
0
    (*m_pEdgeTrack)[nMax-1]=rDragStat.GetNow();
2144
0
    if (rDragStat.GetPageView()!=nullptr) {
2145
0
        ImpFindConnector(rDragStat.GetNow(),*rDragStat.GetPageView(),m_aCon2,this);
2146
0
        rDragStat.GetView()->SetConnectMarker(m_aCon2);
2147
0
    }
2148
0
    SetBoundRectDirty();
2149
0
    m_bSnapRectDirty=true;
2150
0
    ConnectToNode(false,m_aCon2.m_pSdrObj);
2151
0
    *m_pEdgeTrack=ImpCalcEdgeTrack(*m_pEdgeTrack,m_aCon1,m_aCon2,&m_aEdgeInfo);
2152
0
    m_bEdgeTrackDirty=false;
2153
0
    return true;
2154
0
}
2155
2156
bool SdrEdgeObj::EndCreate(SdrDragStat& rDragStat, SdrCreateCmd eCmd)
2157
0
{
2158
0
    bool bOk=(eCmd==SdrCreateCmd::ForceEnd || rDragStat.GetPointCount()>=2);
2159
0
    if (bOk) {
2160
0
        ConnectToNode(true,m_aCon1.m_pSdrObj);
2161
0
        ConnectToNode(false,m_aCon2.m_pSdrObj);
2162
0
        if (rDragStat.GetView()!=nullptr) {
2163
0
            rDragStat.GetView()->HideConnectMarker();
2164
0
        }
2165
0
        ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
2166
0
    }
2167
0
    SetBoundAndSnapRectsDirty();
2168
0
    return bOk;
2169
0
}
2170
2171
bool SdrEdgeObj::BckCreate(SdrDragStat& rDragStat)
2172
0
{
2173
0
    if (rDragStat.GetView()!=nullptr) {
2174
0
        rDragStat.GetView()->HideConnectMarker();
2175
0
    }
2176
0
    return false;
2177
0
}
2178
2179
void SdrEdgeObj::BrkCreate(SdrDragStat& rDragStat)
2180
0
{
2181
0
    if (rDragStat.GetView()!=nullptr) {
2182
0
        rDragStat.GetView()->HideConnectMarker();
2183
0
    }
2184
0
}
2185
2186
basegfx::B2DPolyPolygon SdrEdgeObj::TakeCreatePoly(const SdrDragStat& /*rStatDrag*/) const
2187
0
{
2188
0
    basegfx::B2DPolyPolygon aRetval;
2189
0
    aRetval.append(m_pEdgeTrack->getB2DPolygon());
2190
0
    return aRetval;
2191
0
}
2192
2193
PointerStyle SdrEdgeObj::GetCreatePointer() const
2194
0
{
2195
0
    return PointerStyle::DrawConnect;
2196
0
}
2197
2198
bool SdrEdgeObj::ImpFindConnector(const Point& rPt, const SdrPageView& rPV, SdrObjConnection& rCon, const SdrEdgeObj* pThis, OutputDevice* pOut, SdrDragStat* pDragStat)
2199
0
{
2200
0
    rCon.ResetVars();
2201
0
    if (pOut==nullptr) pOut=rPV.GetView().GetFirstOutputDevice();
2202
0
    if (pOut==nullptr) return false;
2203
0
    SdrObjList* pOL=rPV.GetObjList();
2204
0
    const SdrLayerIDSet& rVisLayer=rPV.GetVisibleLayers();
2205
    // sensitive area of connectors is twice as large as the one of the handles
2206
0
    sal_uInt16 nMarkHdSiz=rPV.GetView().GetMarkHdlSizePixel();
2207
0
    Size aHalfConSiz(nMarkHdSiz,nMarkHdSiz);
2208
0
    if (comphelper::LibreOfficeKit::isActive() && pOut->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
2209
0
        aHalfConSiz=pOut->PixelToLogic(aHalfConSiz, MapMode(MapUnit::Map100thMM));
2210
0
    else
2211
0
        aHalfConSiz=pOut->PixelToLogic(aHalfConSiz);
2212
0
    tools::Rectangle aMouseRect(rPt,rPt);
2213
0
    aMouseRect.AdjustLeft( -(aHalfConSiz.Width()) );
2214
0
    aMouseRect.AdjustTop( -(aHalfConSiz.Height()) );
2215
0
    aMouseRect.AdjustRight(aHalfConSiz.Width() );
2216
0
    aMouseRect.AdjustBottom(aHalfConSiz.Height() );
2217
0
    double fBoundHitTol=static_cast<double>(aHalfConSiz.Width())/2; if (fBoundHitTol==0.0) fBoundHitTol=1.0;
2218
0
    size_t no=pOL->GetObjCount();
2219
0
    bool bFnd = false;
2220
0
    SdrObjConnection aTestCon;
2221
0
    bool bTiledRendering = comphelper::LibreOfficeKit::isActive();
2222
0
    bool bHasRequestedOrdNum = false;
2223
0
    sal_Int32 requestedOrdNum = -1;
2224
2225
0
    if (bTiledRendering && pDragStat)
2226
0
    {
2227
0
        auto& glueOptions = pDragStat->GetGlueOptions();
2228
0
        if (glueOptions.objectOrdNum != -1)
2229
0
        {
2230
0
            requestedOrdNum = glueOptions.objectOrdNum;
2231
0
            bHasRequestedOrdNum = true;
2232
0
        }
2233
0
    }
2234
2235
0
    while (no>0 && !bFnd) {
2236
        // issue: group objects on different layers return LayerID=0!
2237
0
        no--;
2238
0
        SdrObject* pObj=pOL->GetObj(no);
2239
0
        assert(pObj);
2240
0
        if (bHasRequestedOrdNum)
2241
0
        {
2242
0
            if (pObj->GetOrdNumDirect() != static_cast<sal_uInt32>(requestedOrdNum))
2243
0
                continue;
2244
0
        }
2245
0
        if (rVisLayer.IsSet(pObj->GetLayer()) && pObj->IsVisible() &&      // only visible objects
2246
0
            (pThis==nullptr || pObj!=static_cast<SdrObject const *>(pThis))) // don't connect it to itself
2247
0
        {
2248
0
            tools::Rectangle aObjBound(pObj->GetCurrentBoundRect());
2249
0
            if (aObjBound.Overlaps(aMouseRect)) {
2250
0
                aTestCon.ResetVars();
2251
0
                bool bEdge=dynamic_cast<const SdrEdgeObj *>(pObj) != nullptr; // no BestCon for Edge
2252
                // User-defined connectors have absolute priority.
2253
                // After those come Vertex, Corner and center (Best), all prioritized equally.
2254
                // Finally, a HitTest for the object.
2255
0
                const SdrGluePointList* pGPL=pObj->GetGluePointList();
2256
0
                sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
2257
0
                sal_uInt16 nGesAnz=nGluePointCnt+9;
2258
0
                bool bUserFnd = false;
2259
0
                sal_uInt64 nBestDist=0xFFFFFFFF;
2260
0
                for (sal_uInt16 i=0; i<nGesAnz; i++)
2261
0
                {
2262
0
                    bool bUser=i<nGluePointCnt;
2263
0
                    bool bVertex=i>=nGluePointCnt+0 && i<nGluePointCnt+4;
2264
0
                    bool bCorner=i>=nGluePointCnt+4 && i<nGluePointCnt+8;
2265
0
                    bool bCenter=i==nGluePointCnt+8;
2266
0
                    bool bOk = false;
2267
0
                    Point aConPos;
2268
0
                    sal_uInt16 nConNum=i;
2269
0
                    if (bUser) {
2270
0
                        const SdrGluePoint& rGP=(*pGPL)[nConNum];
2271
0
                        aConPos=rGP.GetAbsolutePos(*pObj);
2272
0
                        nConNum=rGP.GetId();
2273
0
                        bOk = true;
2274
0
                    } else if (bVertex && !bUserFnd) {
2275
0
                        nConNum=nConNum-nGluePointCnt;
2276
0
                        SdrGluePoint aPt(pObj->GetVertexGluePoint(nConNum));
2277
0
                        aConPos=aPt.GetAbsolutePos(*pObj);
2278
0
                        bOk = true;
2279
0
                    } else if (bCorner && !bUserFnd) {
2280
0
                        nConNum-=nGluePointCnt+4;
2281
0
                        i+=3;
2282
0
                    }
2283
0
                    else if (bCenter && !bUserFnd && !bEdge)
2284
0
                    {
2285
                        // Suppress default connect at object center
2286
0
                        if(!pThis || !pThis->GetSuppressDefaultConnect())
2287
0
                        {
2288
                            // not the edges!
2289
0
                            nConNum=0;
2290
0
                            aConPos=aObjBound.Center();
2291
0
                            bOk = true;
2292
0
                        }
2293
0
                    }
2294
0
                    if (bOk && aMouseRect.Contains(aConPos)) {
2295
0
                        if (bUser) bUserFnd = true;
2296
0
                        bFnd = true;
2297
0
                        sal_uInt64 nDist=static_cast<sal_uInt64>(std::abs(aConPos.X()-rPt.X()))+static_cast<sal_uInt64>(std::abs(aConPos.Y()-rPt.Y()));
2298
0
                        if (nDist<nBestDist) {
2299
0
                            nBestDist=nDist;
2300
0
                            aTestCon.m_pSdrObj=pObj;
2301
0
                            aTestCon.m_nConId=nConNum;
2302
0
                            aTestCon.m_bAutoCorner=bCorner;
2303
0
                            aTestCon.m_bAutoVertex=bVertex;
2304
0
                            aTestCon.m_bBestConn=false; // bCenter;
2305
0
                            aTestCon.m_bBestVertex=bCenter;
2306
0
                        }
2307
0
                    }
2308
0
                }
2309
                // if no connector is hit, try HitTest again, for BestConnector (=bCenter)
2310
0
                if(!bFnd &&
2311
0
                    !bEdge &&
2312
0
                    SdrObjectPrimitiveHit(*pObj, rPt, {fBoundHitTol, fBoundHitTol}, rPV, &rVisLayer, false))
2313
0
                {
2314
                    // Suppress default connect at object inside bound
2315
0
                    if(!pThis || !pThis->GetSuppressDefaultConnect())
2316
0
                    {
2317
0
                        bFnd = true;
2318
0
                        aTestCon.m_pSdrObj=pObj;
2319
0
                        aTestCon.m_bBestConn=true;
2320
0
                    }
2321
0
                }
2322
0
                if (bFnd) {
2323
0
                    aMouseRect.AdjustLeft( -fBoundHitTol );
2324
0
                    aMouseRect.AdjustTop( -fBoundHitTol );
2325
0
                    aMouseRect.AdjustRight(fBoundHitTol );
2326
0
                    aMouseRect.AdjustBottom(fBoundHitTol );
2327
0
                }
2328
2329
0
            }
2330
0
        }
2331
0
    }
2332
0
    rCon=aTestCon;
2333
0
    return bFnd;
2334
0
}
2335
2336
void SdrEdgeObj::NbcSetSnapRect(const tools::Rectangle& rRect)
2337
4.31k
{
2338
4.31k
    const tools::Rectangle aOld(GetSnapRect());
2339
2340
4.31k
    if(aOld == rRect)
2341
462
        return;
2342
2343
3.84k
    if (getRectangle().IsEmpty() && 0 == m_pEdgeTrack->GetPointCount())
2344
1.62k
    {
2345
        // #i110629# When initializing, do not scale on empty Rectangle; this
2346
        // will mirror the underlying text object (!)
2347
1.62k
        setRectangle(rRect);
2348
1.62k
        maSnapRect = rRect;
2349
1.62k
    }
2350
2.22k
    else
2351
2.22k
    {
2352
2.22k
        tools::Long nMulX = rRect.Right()  - rRect.Left();
2353
2.22k
        tools::Long nDivX = aOld.Right()   - aOld.Left();
2354
2.22k
        tools::Long nMulY = rRect.Bottom() - rRect.Top();
2355
2.22k
        tools::Long nDivY = aOld.Bottom()  - aOld.Top();
2356
2.22k
        if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2357
2.22k
        if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2358
2.22k
        double aX = double(nMulX) / nDivX;
2359
2.22k
        double aY = double(nMulY) / nDivY;
2360
2.22k
        NbcResize(aOld.TopLeft(), aX, aY);
2361
2.22k
        NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2362
2.22k
    }
2363
3.84k
}
2364
2365
void SdrEdgeObj::NbcMove(const Size& rSiz)
2366
2.30k
{
2367
2.30k
    SdrTextObj::NbcMove(rSiz);
2368
2.30k
    MoveXPoly(*m_pEdgeTrack,rSiz);
2369
2.30k
}
2370
2371
void SdrEdgeObj::NbcResize(const Point& rRefPnt, double aXFact, double aYFact)
2372
3.48k
{
2373
3.48k
    SdrTextObj::NbcResize(rRefPnt,aXFact,aXFact);
2374
3.48k
    ResizeXPoly(*m_pEdgeTrack,rRefPnt,aXFact,aYFact);
2375
2376
    // if resize is not from paste, forget user distances
2377
3.48k
    if (!getSdrModelFromSdrObject().IsPasteResize())
2378
3.48k
    {
2379
3.48k
        m_aEdgeInfo.m_aObj1Line2 = Point();
2380
3.48k
        m_aEdgeInfo.m_aObj1Line3 = Point();
2381
3.48k
        m_aEdgeInfo.m_aObj2Line2 = Point();
2382
3.48k
        m_aEdgeInfo.m_aObj2Line3 = Point();
2383
3.48k
        m_aEdgeInfo.m_aMiddleLine = Point();
2384
3.48k
    }
2385
3.48k
}
2386
2387
// #i54102# added rotation support
2388
void SdrEdgeObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
2389
268
{
2390
268
    if(m_bEdgeTrackUserDefined)
2391
268
    {
2392
        // #i120437# special handling when track is imported, apply
2393
        // transformation directly to imported track.
2394
268
        SdrTextObj::NbcRotate(rRef, nAngle, sn, cs);
2395
268
        RotateXPoly(*m_pEdgeTrack, rRef, sn, cs);
2396
268
    }
2397
0
    else
2398
0
    {
2399
        // handle start and end point if not connected
2400
0
        const bool bCon1(nullptr != m_aCon1.m_pSdrObj && m_aCon1.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2401
0
        const bool bCon2(nullptr != m_aCon2.m_pSdrObj && m_aCon2.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2402
2403
0
        if(!bCon1 && m_pEdgeTrack)
2404
0
        {
2405
0
            RotatePoint((*m_pEdgeTrack)[0],rRef,sn,cs);
2406
0
            ImpDirtyEdgeTrack();
2407
0
        }
2408
2409
0
        if(!bCon2 && m_pEdgeTrack)
2410
0
        {
2411
0
            sal_uInt16 nPointCount = m_pEdgeTrack->GetPointCount();
2412
0
            RotatePoint((*m_pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,sn,cs);
2413
0
            ImpDirtyEdgeTrack();
2414
0
        }
2415
0
    }
2416
268
}
2417
2418
// #i54102# added mirror support
2419
void SdrEdgeObj::NbcMirror(const Point& rRef1, const Point& rRef2)
2420
593
{
2421
593
    if(m_bEdgeTrackUserDefined)
2422
593
    {
2423
        // #i120437# special handling when track is imported, apply
2424
        // transformation directly to imported track.
2425
593
        SdrTextObj::NbcMirror(rRef1, rRef2);
2426
593
        MirrorXPoly(*m_pEdgeTrack, rRef1, rRef2);
2427
593
    }
2428
0
    else
2429
0
    {
2430
        // handle start and end point if not connected
2431
0
        const bool bCon1(nullptr != m_aCon1.m_pSdrObj && m_aCon1.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2432
0
        const bool bCon2(nullptr != m_aCon2.m_pSdrObj && m_aCon2.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2433
2434
0
        if(!bCon1 && m_pEdgeTrack)
2435
0
        {
2436
0
            MirrorPoint((*m_pEdgeTrack)[0],rRef1,rRef2);
2437
0
            ImpDirtyEdgeTrack();
2438
0
        }
2439
2440
0
        if(!bCon2 && m_pEdgeTrack)
2441
0
        {
2442
0
            sal_uInt16 nPointCount = m_pEdgeTrack->GetPointCount();
2443
0
            MirrorPoint((*m_pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef1,rRef2);
2444
0
            ImpDirtyEdgeTrack();
2445
0
        }
2446
0
    }
2447
593
}
2448
2449
// #i54102# added shear support
2450
void SdrEdgeObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
2451
0
{
2452
0
    if(m_bEdgeTrackUserDefined)
2453
0
    {
2454
        // #i120437# special handling when track is imported, apply
2455
        // transformation directly to imported track.
2456
0
        SdrTextObj::NbcShear(rRef, nAngle, tn, bVShear);
2457
0
        ShearXPoly(*m_pEdgeTrack, rRef, tn, bVShear);
2458
0
    }
2459
0
    else
2460
0
    {
2461
        // handle start and end point if not connected
2462
0
        const bool bCon1(nullptr != m_aCon1.m_pSdrObj && m_aCon1.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2463
0
        const bool bCon2(nullptr != m_aCon2.m_pSdrObj && m_aCon2.m_pSdrObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2464
2465
0
        if(!bCon1 && m_pEdgeTrack)
2466
0
        {
2467
0
            ShearPoint((*m_pEdgeTrack)[0],rRef,tn,bVShear);
2468
0
            ImpDirtyEdgeTrack();
2469
0
        }
2470
2471
0
        if(!bCon2 && m_pEdgeTrack)
2472
0
        {
2473
0
            sal_uInt16 nPointCount = m_pEdgeTrack->GetPointCount();
2474
0
            ShearPoint((*m_pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,tn,bVShear);
2475
0
            ImpDirtyEdgeTrack();
2476
0
        }
2477
0
    }
2478
0
}
2479
2480
rtl::Reference<SdrObject> SdrEdgeObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
2481
0
{
2482
0
    basegfx::B2DPolyPolygon aPolyPolygon;
2483
0
    aPolyPolygon.append(m_pEdgeTrack->getB2DPolygon());
2484
0
    rtl::Reference<SdrObject> pRet = ImpConvertMakeObj(aPolyPolygon, false, bBezier);
2485
2486
0
    if(bAddText)
2487
0
    {
2488
0
        pRet = ImpConvertAddText(std::move(pRet), bBezier);
2489
0
    }
2490
2491
0
    return pRet;
2492
0
}
2493
2494
sal_uInt32 SdrEdgeObj::GetSnapPointCount() const
2495
0
{
2496
0
    return 2;
2497
0
}
2498
2499
Point SdrEdgeObj::GetSnapPoint(sal_uInt32 i) const
2500
0
{
2501
0
    const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
2502
0
    sal_uInt16 nCount=m_pEdgeTrack->GetPointCount();
2503
0
    if (i==0) return (*m_pEdgeTrack)[0];
2504
0
    else return (*m_pEdgeTrack)[nCount-1];
2505
0
}
2506
2507
bool SdrEdgeObj::IsPolyObj() const
2508
204
{
2509
204
    return false;
2510
204
}
2511
2512
sal_uInt32 SdrEdgeObj::GetPointCount() const
2513
0
{
2514
0
    return 0;
2515
0
}
2516
2517
Point SdrEdgeObj::GetPoint(sal_uInt32 i) const
2518
0
{
2519
0
    const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
2520
0
    sal_uInt16 nCount=m_pEdgeTrack->GetPointCount();
2521
0
    if (0 == i)
2522
0
        return (*m_pEdgeTrack)[0];
2523
0
    else
2524
0
        return (*m_pEdgeTrack)[nCount-1];
2525
0
}
2526
2527
void SdrEdgeObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
2528
18.6k
{
2529
    // TODO: Need an implementation to connect differently.
2530
18.6k
    ImpUndirtyEdgeTrack();
2531
18.6k
    sal_uInt16 nCount=m_pEdgeTrack->GetPointCount();
2532
18.6k
    if (0 == i)
2533
9.31k
        (*m_pEdgeTrack)[0]=rPnt;
2534
18.6k
    if (1 == i)
2535
9.31k
        (*m_pEdgeTrack)[nCount-1]=rPnt;
2536
18.6k
    SetEdgeTrackDirty();
2537
18.6k
    SetBoundAndSnapRectsDirty();
2538
18.6k
}
2539
2540
SdrEdgeObjGeoData::SdrEdgeObjGeoData()
2541
0
    : m_pEdgeTrack(std::in_place)
2542
0
    , m_bEdgeTrackDirty(false)
2543
0
    , m_bEdgeTrackUserDefined(false)
2544
0
{
2545
0
}
2546
2547
SdrEdgeObjGeoData::~SdrEdgeObjGeoData()
2548
0
{
2549
0
}
2550
2551
std::unique_ptr<SdrObjGeoData> SdrEdgeObj::NewGeoData() const
2552
0
{
2553
0
    return std::make_unique<SdrEdgeObjGeoData>();
2554
0
}
2555
2556
void SdrEdgeObj::SaveGeoData(SdrObjGeoData& rGeo) const
2557
0
{
2558
0
    SdrTextObj::SaveGeoData(rGeo);
2559
0
    SdrEdgeObjGeoData& rEGeo=static_cast<SdrEdgeObjGeoData&>(rGeo);
2560
0
    rEGeo.m_aCon1          =m_aCon1;
2561
0
    rEGeo.m_aCon2          =m_aCon2;
2562
0
    *rEGeo.m_pEdgeTrack    =*m_pEdgeTrack;
2563
0
    rEGeo.m_bEdgeTrackDirty=m_bEdgeTrackDirty;
2564
0
    rEGeo.m_bEdgeTrackUserDefined=m_bEdgeTrackUserDefined;
2565
0
    rEGeo.m_aEdgeInfo      =m_aEdgeInfo;
2566
0
}
2567
2568
void SdrEdgeObj::RestoreGeoData(const SdrObjGeoData& rGeo)
2569
0
{
2570
0
    SdrTextObj::RestoreGeoData(rGeo);
2571
0
    const SdrEdgeObjGeoData& rEGeo=static_cast<const SdrEdgeObjGeoData&>(rGeo);
2572
0
    if (m_aCon1.m_pSdrObj!=rEGeo.m_aCon1.m_pSdrObj) {
2573
0
        if (m_aCon1.m_pSdrObj!=nullptr) m_aCon1.m_pSdrObj->RemoveListener(*this);
2574
0
        m_aCon1=rEGeo.m_aCon1;
2575
0
        if (m_aCon1.m_pSdrObj!=nullptr) m_aCon1.m_pSdrObj->AddListener(*this);
2576
0
    }
2577
0
    else
2578
0
        m_aCon1=rEGeo.m_aCon1;
2579
2580
0
    if (m_aCon2.m_pSdrObj!=rEGeo.m_aCon2.m_pSdrObj) {
2581
0
        if (m_aCon2.m_pSdrObj!=nullptr) m_aCon2.m_pSdrObj->RemoveListener(*this);
2582
0
        m_aCon2=rEGeo.m_aCon2;
2583
0
        if (m_aCon2.m_pSdrObj!=nullptr) m_aCon2.m_pSdrObj->AddListener(*this);
2584
0
    }
2585
0
    else
2586
0
        m_aCon2=rEGeo.m_aCon2;
2587
2588
0
    *m_pEdgeTrack    =*rEGeo.m_pEdgeTrack;
2589
0
    m_bEdgeTrackDirty=rEGeo.m_bEdgeTrackDirty;
2590
0
    m_bEdgeTrackUserDefined=rEGeo.m_bEdgeTrackUserDefined;
2591
0
    m_aEdgeInfo      =rEGeo.m_aEdgeInfo;
2592
0
}
2593
2594
Point SdrEdgeObj::GetTailPoint( bool bTail ) const
2595
0
{
2596
0
    if( m_pEdgeTrack && m_pEdgeTrack->GetPointCount()!=0)
2597
0
    {
2598
0
        const XPolygon& rTrack0 = *m_pEdgeTrack;
2599
0
        if(bTail)
2600
0
        {
2601
0
            return rTrack0[0];
2602
0
        }
2603
0
        else
2604
0
        {
2605
0
            const sal_uInt16 nSiz = rTrack0.GetPointCount() - 1;
2606
0
            return rTrack0[nSiz];
2607
0
        }
2608
0
    }
2609
0
    else
2610
0
    {
2611
0
        if(bTail)
2612
0
            return getOutRectangle().TopLeft();
2613
0
        else
2614
0
            return getOutRectangle().BottomRight();
2615
0
    }
2616
2617
0
}
2618
2619
void SdrEdgeObj::SetTailPoint( bool bTail, const Point& rPt )
2620
3.24k
{
2621
3.24k
    ImpSetTailPoint( bTail, rPt );
2622
3.24k
    SetChanged();
2623
3.24k
}
2624
2625
/** this method is used by the api to set a gluepoint for a connection
2626
    nId == -1 :     The best default point is automatically chosen
2627
    0 <= nId <= 3 : One of the default points is chosen
2628
    nId >= 4 :      A user defined gluepoint is chosen
2629
*/
2630
void SdrEdgeObj::setGluePointIndex( bool bTail, sal_Int32 nIndex /* = -1 */ )
2631
12.8k
{
2632
12.8k
    SdrObjConnection& rConn1 = GetConnection( bTail );
2633
2634
12.8k
    rConn1.SetAutoVertex( nIndex >= 0 && nIndex <= 3 );
2635
12.8k
    rConn1.SetBestConnection( nIndex < 0 );
2636
12.8k
    rConn1.SetBestVertex( nIndex < 0 );
2637
2638
12.8k
    if( nIndex > 3 )
2639
292
    {
2640
292
        nIndex -= 3;        // the start api index is 0, whereas the implementation in svx starts from 1
2641
2642
        // for user defined gluepoints we have
2643
        // to get the id for this index first
2644
292
        const SdrGluePointList* pList = rConn1.GetSdrObject() ? rConn1.GetSdrObject()->GetGluePointList() : nullptr;
2645
292
        if( pList == nullptr || SDRGLUEPOINT_NOTFOUND == pList->FindGluePoint(static_cast<sal_uInt16>(nIndex)) )
2646
0
            return;
2647
292
    }
2648
12.5k
    else if( nIndex < 0 )
2649
6.53k
    {
2650
6.53k
        nIndex = 0;
2651
6.53k
    }
2652
2653
12.8k
    rConn1.SetConnectorId( static_cast<sal_uInt16>(nIndex) );
2654
2655
12.8k
    SetChanged();
2656
12.8k
    SetBoundAndSnapRectsDirty();
2657
12.8k
    ImpRecalcEdgeTrack();
2658
12.8k
}
2659
2660
/** this method is used by the api to return a gluepoint id for a connection.
2661
    See setGluePointId for possible return values */
2662
sal_Int32 SdrEdgeObj::getGluePointIndex( bool bTail )
2663
0
{
2664
0
    SdrObjConnection& rConn1 = GetConnection( bTail );
2665
0
    sal_Int32 nId = -1;
2666
0
    if( !rConn1.IsBestConnection() )
2667
0
    {
2668
0
        nId = rConn1.GetConnectorId();
2669
0
        if( !rConn1.IsAutoVertex() )
2670
0
            nId += 3;       // the start api index is 0, whereas the implementation in svx starts from 1
2671
0
    }
2672
0
    return nId;
2673
0
}
2674
2675
// Implementation was missing; edge track needs to be invalidated additionally.
2676
void SdrEdgeObj::NbcSetAnchorPos(const Point& rPnt)
2677
0
{
2678
    // call parent functionality
2679
0
    SdrTextObj::NbcSetAnchorPos(rPnt);
2680
2681
    // Additionally, invalidate edge track
2682
0
    ImpDirtyEdgeTrack();
2683
0
}
2684
2685
bool SdrEdgeObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
2686
0
{
2687
    // use base method from SdrObject, it's not rotatable and
2688
    // a call to GetSnapRect() is used. That's what we need for Connector.
2689
0
    return SdrObject::TRGetBaseGeometry(rMatrix, rPolyPolygon);
2690
0
}
2691
2692
void SdrEdgeObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
2693
0
{
2694
    // where appropriate take care for existing connections. For now, just use the
2695
    // implementation from SdrObject.
2696
0
    SdrObject::TRSetBaseGeometry(rMatrix, rPolyPolygon);
2697
0
}
2698
2699
// for geometry access
2700
::basegfx::B2DPolygon SdrEdgeObj::getEdgeTrack() const
2701
0
{
2702
0
    if(m_bEdgeTrackDirty)
2703
0
    {
2704
0
        const_cast< SdrEdgeObj* >(this)->ImpRecalcEdgeTrack();
2705
0
    }
2706
2707
0
    if(m_pEdgeTrack)
2708
0
    {
2709
0
        return m_pEdgeTrack->getB2DPolygon();
2710
0
    }
2711
0
    else
2712
0
    {
2713
0
        return ::basegfx::B2DPolygon();
2714
0
    }
2715
0
}
2716
2717
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */