1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <svx/dialmgr.hxx>
21 #include <svx/strings.hrc>
22 #include <osl/diagnose.h>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <basegfx/polygon/b2dpolygon.hxx>
26 #include <svl/hint.hxx>
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>
49 void SdrObjConnection::ResetVars()
59 bool SdrObjConnection::TakeGluePoint(SdrGluePoint
& rGP
) const
62 if (m_pSdrObj
!=nullptr) { // one object has to be docked already!
64 rGP
=m_pSdrObj
->GetVertexGluePoint(m_nConId
);
66 } else if (m_bAutoCorner
) {
67 rGP
=m_pSdrObj
->GetCornerGluePoint(m_nConId
);
70 const SdrGluePointList
* pGPL
=m_pSdrObj
->GetGluePointList();
72 sal_uInt16 nNum
=pGPL
->FindGluePoint(m_nConId
);
73 if (nNum
!=SDRGLUEPOINT_NOTFOUND
) {
81 Point
aPt(rGP
.GetAbsolutePos(*m_pSdrObj
));
88 Point
& SdrEdgeInfoRec::ImpGetLineOffsetPoint(SdrEdgeLineCode eLineCode
)
91 case SdrEdgeLineCode::Obj1Line2
: return m_aObj1Line2
;
92 case SdrEdgeLineCode::Obj1Line3
: return m_aObj1Line3
;
93 case SdrEdgeLineCode::Obj2Line2
: return m_aObj2Line2
;
94 case SdrEdgeLineCode::Obj2Line3
: return m_aObj2Line3
;
95 case SdrEdgeLineCode::MiddleLine
: return m_aMiddleLine
;
100 sal_uInt16
SdrEdgeInfoRec::ImpGetPolyIdx(SdrEdgeLineCode eLineCode
, const XPolygon
& rXP
) const
103 case SdrEdgeLineCode::Obj1Line2
: return 1;
104 case SdrEdgeLineCode::Obj1Line3
: return 2;
105 case SdrEdgeLineCode::Obj2Line2
: return rXP
.GetPointCount()-3;
106 case SdrEdgeLineCode::Obj2Line3
: return rXP
.GetPointCount()-4;
107 case SdrEdgeLineCode::MiddleLine
: return m_nMiddleLine
;
112 bool SdrEdgeInfoRec::ImpIsHorzLine(SdrEdgeLineCode eLineCode
, const XPolygon
& rXP
) const
114 sal_uInt16 nIdx
=ImpGetPolyIdx(eLineCode
,rXP
);
115 bool bHorz
=m_nAngle1
==0 || m_nAngle1
==18000;
116 if (eLineCode
==SdrEdgeLineCode::Obj2Line2
|| eLineCode
==SdrEdgeLineCode::Obj2Line3
) {
117 nIdx
=rXP
.GetPointCount()-nIdx
;
118 bHorz
=m_nAngle2
==0 || m_nAngle2
==18000;
120 if ((nIdx
& 1)==1) bHorz
=!bHorz
;
124 void SdrEdgeInfoRec::ImpSetLineOffset(SdrEdgeLineCode eLineCode
, const XPolygon
& rXP
, tools::Long nVal
)
126 Point
& rPt
=ImpGetLineOffsetPoint(eLineCode
);
127 if (ImpIsHorzLine(eLineCode
,rXP
)) rPt
.setY(nVal
);
128 else rPt
.setX(nVal
);
131 tools::Long
SdrEdgeInfoRec::ImpGetLineOffset(SdrEdgeLineCode eLineCode
, const XPolygon
& rXP
) const
133 const Point
& rPt
= const_cast<SdrEdgeInfoRec
*>(this)->ImpGetLineOffsetPoint(eLineCode
);
134 if (ImpIsHorzLine(eLineCode
,rXP
))
141 // BaseProperties section
143 std::unique_ptr
<sdr::properties::BaseProperties
> SdrEdgeObj::CreateObjectSpecificProperties()
145 return std::make_unique
<sdr::properties::ConnectorProperties
>(*this);
149 // DrawContact section
151 std::unique_ptr
<sdr::contact::ViewContact
> SdrEdgeObj::CreateObjectSpecificViewContact()
153 return std::make_unique
<sdr::contact::ViewContactOfSdrEdgeObj
>(*this);
157 SdrEdgeObj::SdrEdgeObj(SdrModel
& rSdrModel
)
158 : SdrTextObj(rSdrModel
),
159 m_nNotifyingCount(0),
160 m_bEdgeTrackDirty(false),
161 m_bEdgeTrackUserDefined(false),
162 // Default is to allow default connects
163 mbSuppressDefaultConnect(false),
164 mbBoundRectCalculationRunning(false),
169 m_pEdgeTrack
= XPolygon();
172 SdrEdgeObj::SdrEdgeObj(SdrModel
& rSdrModel
, SdrEdgeObj
const & rSource
)
173 : SdrTextObj(rSdrModel
, rSource
),
174 m_nNotifyingCount(0),
175 m_bEdgeTrackDirty(false),
176 m_bEdgeTrackUserDefined(false),
177 // Default is to allow default connects
178 mbSuppressDefaultConnect(false),
179 mbBoundRectCalculationRunning(false),
182 m_bClosedObj
= false;
184 m_pEdgeTrack
= rSource
.m_pEdgeTrack
;
185 m_bEdgeTrackDirty
=rSource
.m_bEdgeTrackDirty
;
186 m_aCon1
=rSource
.m_aCon1
;
187 m_aCon2
=rSource
.m_aCon2
;
188 m_aCon1
.m_pSdrObj
=nullptr;
189 m_aCon2
.m_pSdrObj
=nullptr;
190 m_aEdgeInfo
=rSource
.m_aEdgeInfo
;
193 SdrEdgeObj::~SdrEdgeObj()
195 SdrEdgeObj::DisconnectFromNode(true);
196 SdrEdgeObj::DisconnectFromNode(false);
199 void SdrEdgeObj::handlePageChange(SdrPage
* pOldPage
, SdrPage
* pNewPage
)
202 SdrTextObj::handlePageChange(pOldPage
, pNewPage
);
204 if(nullptr != GetConnection(true).GetSdrObject() || nullptr != GetConnection(false).GetSdrObject())
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();
213 void SdrEdgeObj::ImpSetAttrToEdgeInfo()
215 const SfxItemSet
& rSet
= GetObjectItemSet();
216 SdrEdgeKind eKind
= rSet
.Get(SDRATTR_EDGEKIND
).GetValue();
217 sal_Int32 nVal1
= rSet
.Get(SDRATTR_EDGELINE1DELTA
).GetValue();
218 sal_Int32 nVal2
= rSet
.Get(SDRATTR_EDGELINE2DELTA
).GetValue();
219 sal_Int32 nVal3
= rSet
.Get(SDRATTR_EDGELINE3DELTA
).GetValue();
221 if(eKind
== SdrEdgeKind::OrthoLines
|| eKind
== SdrEdgeKind::Bezier
)
223 sal_Int32 nVals
[3] = { nVal1
, nVal2
, nVal3
};
226 if(m_aEdgeInfo
.m_nObj1Lines
>= 2 && n
< 3)
228 m_aEdgeInfo
.ImpSetLineOffset(SdrEdgeLineCode::Obj1Line2
, *m_pEdgeTrack
, nVals
[n
]);
232 if(m_aEdgeInfo
.m_nObj1Lines
>= 3 && n
< 3)
234 m_aEdgeInfo
.ImpSetLineOffset(SdrEdgeLineCode::Obj1Line3
, *m_pEdgeTrack
, nVals
[n
]);
238 if(m_aEdgeInfo
.m_nMiddleLine
!= 0xFFFF && n
< 3)
240 m_aEdgeInfo
.ImpSetLineOffset(SdrEdgeLineCode::MiddleLine
, *m_pEdgeTrack
, nVals
[n
]);
244 if(m_aEdgeInfo
.m_nObj2Lines
>= 3 && n
< 3)
246 m_aEdgeInfo
.ImpSetLineOffset(SdrEdgeLineCode::Obj2Line3
, *m_pEdgeTrack
, nVals
[n
]);
250 if(m_aEdgeInfo
.m_nObj2Lines
>= 2 && n
< 3)
252 m_aEdgeInfo
.ImpSetLineOffset(SdrEdgeLineCode::Obj2Line2
, *m_pEdgeTrack
, nVals
[n
]);
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 if (rSet
.HasItem(SDRATTR_EDGEOOXMLCURVE
))
259 m_aEdgeInfo
.m_bUseOOXMLCurve
= rSet
.Get(SDRATTR_EDGEOOXMLCURVE
).GetValue();
261 else if(eKind
== SdrEdgeKind::ThreeLines
)
263 bool bHor1
= m_aEdgeInfo
.m_nAngle1
== 0 || m_aEdgeInfo
.m_nAngle1
== 18000;
264 bool bHor2
= m_aEdgeInfo
.m_nAngle2
== 0 || m_aEdgeInfo
.m_nAngle2
== 18000;
268 m_aEdgeInfo
.m_aObj1Line2
.setX( nVal1
);
272 m_aEdgeInfo
.m_aObj1Line2
.setY( nVal1
);
277 m_aEdgeInfo
.m_aObj2Line2
.setX( nVal2
);
281 m_aEdgeInfo
.m_aObj2Line2
.setY( nVal2
);
288 void SdrEdgeObj::ImpSetEdgeInfoToAttr()
290 const SfxItemSet
& rSet
= GetObjectItemSet();
291 SdrEdgeKind eKind
= rSet
.Get(SDRATTR_EDGEKIND
).GetValue();
292 sal_Int32 nValCnt
= rSet
.Get(SDRATTR_EDGELINEDELTACOUNT
).GetValue();
293 sal_Int32 nVal1
= rSet
.Get(SDRATTR_EDGELINE1DELTA
).GetValue();
294 sal_Int32 nVal2
= rSet
.Get(SDRATTR_EDGELINE2DELTA
).GetValue();
295 sal_Int32 nVal3
= rSet
.Get(SDRATTR_EDGELINE3DELTA
).GetValue();
296 sal_Int32 nVals
[3] = { nVal1
, nVal2
, nVal3
};
299 if(eKind
== SdrEdgeKind::OrthoLines
|| eKind
== SdrEdgeKind::Bezier
)
301 if(m_aEdgeInfo
.m_nObj1Lines
>= 2 && n
< 3)
303 nVals
[n
] = m_aEdgeInfo
.ImpGetLineOffset(SdrEdgeLineCode::Obj1Line2
, *m_pEdgeTrack
);
307 if(m_aEdgeInfo
.m_nObj1Lines
>= 3 && n
< 3)
309 nVals
[n
] = m_aEdgeInfo
.ImpGetLineOffset(SdrEdgeLineCode::Obj1Line3
, *m_pEdgeTrack
);
313 if(m_aEdgeInfo
.m_nMiddleLine
!= 0xFFFF && n
< 3)
315 nVals
[n
] = m_aEdgeInfo
.ImpGetLineOffset(SdrEdgeLineCode::MiddleLine
, *m_pEdgeTrack
);
319 if(m_aEdgeInfo
.m_nObj2Lines
>= 3 && n
< 3)
321 nVals
[n
] = m_aEdgeInfo
.ImpGetLineOffset(SdrEdgeLineCode::Obj2Line3
, *m_pEdgeTrack
);
325 if(m_aEdgeInfo
.m_nObj2Lines
>= 2 && n
< 3)
327 nVals
[n
] = m_aEdgeInfo
.ImpGetLineOffset(SdrEdgeLineCode::Obj2Line2
, *m_pEdgeTrack
);
331 else if(eKind
== SdrEdgeKind::ThreeLines
)
333 bool bHor1
= m_aEdgeInfo
.m_nAngle1
== 0 || m_aEdgeInfo
.m_nAngle1
== 18000;
334 bool bHor2
= m_aEdgeInfo
.m_nAngle2
== 0 || m_aEdgeInfo
.m_nAngle2
== 18000;
337 nVals
[0] = bHor1
? m_aEdgeInfo
.m_aObj1Line2
.X() : m_aEdgeInfo
.m_aObj1Line2
.Y();
338 nVals
[1] = bHor2
? m_aEdgeInfo
.m_aObj2Line2
.X() : m_aEdgeInfo
.m_aObj2Line2
.Y();
341 if(!(n
!= nValCnt
|| nVals
[0] != nVal1
|| nVals
[1] != nVal2
|| nVals
[2] != nVal3
))
344 // Here no more notifying is necessary, just local changes are OK.
347 GetProperties().SetObjectItemDirect(SdrEdgeLineDeltaCountItem(n
));
350 if(nVals
[0] != nVal1
)
352 GetProperties().SetObjectItemDirect(makeSdrEdgeLine1DeltaItem(nVals
[0]));
355 if(nVals
[1] != nVal2
)
357 GetProperties().SetObjectItemDirect(makeSdrEdgeLine2DeltaItem(nVals
[1]));
360 if(nVals
[2] != nVal3
)
362 GetProperties().SetObjectItemDirect(makeSdrEdgeLine3DeltaItem(nVals
[2]));
367 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE3DELTA
);
372 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE2DELTA
);
377 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE1DELTA
);
380 GetProperties().SetObjectItemDirect(
381 SfxBoolItem(SDRATTR_EDGEOOXMLCURVE
, m_aEdgeInfo
.m_bUseOOXMLCurve
));
384 void SdrEdgeObj::TakeObjInfo(SdrObjTransformInfoRec
& rInfo
) const
386 // #i54102# allow rotation, mirror and shear
387 rInfo
.bRotateFreeAllowed
= true;
388 rInfo
.bRotate90Allowed
= true;
389 rInfo
.bMirrorFreeAllowed
= true;
390 rInfo
.bMirror45Allowed
= true;
391 rInfo
.bMirror90Allowed
= true;
392 rInfo
.bTransparenceAllowed
= false;
393 rInfo
.bShearAllowed
= true;
394 rInfo
.bEdgeRadiusAllowed
= false;
395 bool bCanConv
=!HasText() || ImpCanConvTextToCurve();
396 rInfo
.bCanConvToPath
=bCanConv
;
397 rInfo
.bCanConvToPoly
=bCanConv
;
398 rInfo
.bCanConvToContour
= (rInfo
.bCanConvToPoly
|| LineGeometryUsageIsNecessary());
401 SdrObjKind
SdrEdgeObj::GetObjIdentifier() const
403 return SdrObjKind::Edge
;
406 const tools::Rectangle
& SdrEdgeObj::GetCurrentBoundRect() const
408 if(m_bEdgeTrackDirty
)
410 const_cast<SdrEdgeObj
*>(this)->ImpRecalcEdgeTrack();
413 return SdrTextObj::GetCurrentBoundRect();
416 const tools::Rectangle
& SdrEdgeObj::GetSnapRect() const
418 if(m_bEdgeTrackDirty
)
420 const_cast<SdrEdgeObj
*>(this)->ImpRecalcEdgeTrack();
423 return SdrTextObj::GetSnapRect();
426 void SdrEdgeObj::RecalcSnapRect()
428 maSnapRect
=m_pEdgeTrack
->GetBoundRect();
431 void SdrEdgeObj::TakeUnrotatedSnapRect(tools::Rectangle
& rRect
) const
436 SdrGluePoint
SdrEdgeObj::GetVertexGluePoint(sal_uInt16 nNum
) const
439 sal_uInt16 nPointCount
=m_pEdgeTrack
->GetPointCount();
442 Point aOfs
= GetSnapRect().Center();
443 if (nNum
==2 && GetConnectedNode(true)==nullptr) aPt
=(*m_pEdgeTrack
)[0];
444 else if (nNum
==3 && GetConnectedNode(false)==nullptr) aPt
=(*m_pEdgeTrack
)[nPointCount
-1];
446 if ((nPointCount
& 1) ==1) {
447 aPt
=(*m_pEdgeTrack
)[nPointCount
/2];
449 Point
aPt1((*m_pEdgeTrack
)[nPointCount
/2-1]);
450 Point
aPt2((*m_pEdgeTrack
)[nPointCount
/2]);
452 aPt1
.setX( aPt1
.X() / 2 );
453 aPt1
.setY( aPt1
.Y() / 2 );
459 SdrGluePoint
aGP(aPt
);
460 aGP
.SetPercent(false);
464 SdrGluePoint
SdrEdgeObj::GetCornerGluePoint(sal_uInt16 nNum
) const
466 return GetVertexGluePoint(nNum
);
469 const SdrGluePointList
* SdrEdgeObj::GetGluePointList() const
471 return nullptr; // no user defined gluepoints for connectors
474 SdrGluePointList
* SdrEdgeObj::ForceGluePointList()
476 return nullptr; // no user defined gluepoints for connectors
479 void SdrEdgeObj::ConnectToNode(bool bTail1
, SdrObject
* pObj
)
481 SdrObjConnection
& rCon
=GetConnection(bTail1
);
482 DisconnectFromNode(bTail1
);
484 pObj
->AddListener(*this);
487 // #i120437# If connection is set, reset bEdgeTrackUserDefined
488 m_bEdgeTrackUserDefined
= false;
494 void SdrEdgeObj::DisconnectFromNode(bool bTail1
)
496 SdrObjConnection
& rCon
=GetConnection(bTail1
);
497 if (rCon
.m_pSdrObj
!=nullptr) {
498 rCon
.m_pSdrObj
->RemoveListener(*this);
499 rCon
.m_pSdrObj
=nullptr;
503 SdrObject
* SdrEdgeObj::GetConnectedNode(bool bTail1
) const
505 SdrObject
* pObj(GetConnection(bTail1
).m_pSdrObj
);
508 && (pObj
->getSdrPageFromSdrObject() != getSdrPageFromSdrObject() || !pObj
->IsInserted()))
516 bool SdrEdgeObj::CheckNodeConnection(bool bTail1
) const
519 const SdrObjConnection
& rCon
=GetConnection(bTail1
);
520 sal_uInt16 nPointCount
=m_pEdgeTrack
->GetPointCount();
522 if(nullptr != rCon
.m_pSdrObj
&& rCon
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject() && 0 != nPointCount
)
524 const SdrGluePointList
* pGPL
=rCon
.m_pSdrObj
->GetGluePointList();
525 sal_uInt16 nGluePointCnt
=pGPL
==nullptr ? 0 : pGPL
->GetCount();
526 sal_uInt16 nGesAnz
=nGluePointCnt
+8;
527 Point
aTail(bTail1
? (*m_pEdgeTrack
)[0] : (*m_pEdgeTrack
)[sal_uInt16(nPointCount
-1)]);
528 for (sal_uInt16 i
=0; i
<nGesAnz
&& !bRet
; i
++) {
529 if (i
<nGluePointCnt
) { // UserDefined
530 bRet
=aTail
==(*pGPL
)[i
].GetAbsolutePos(*rCon
.m_pSdrObj
);
531 } else if (i
<nGluePointCnt
+4) { // Vertex
532 SdrGluePoint
aPt(rCon
.m_pSdrObj
->GetVertexGluePoint(i
-nGluePointCnt
));
533 bRet
=aTail
==aPt
.GetAbsolutePos(*rCon
.m_pSdrObj
);
535 SdrGluePoint
aPt(rCon
.m_pSdrObj
->GetCornerGluePoint(i
-nGluePointCnt
-4));
536 bRet
=aTail
==aPt
.GetAbsolutePos(*rCon
.m_pSdrObj
);
543 void SdrEdgeObj::ImpSetTailPoint(bool bTail1
, const Point
& rPt
)
545 sal_uInt16 nPointCount
=m_pEdgeTrack
->GetPointCount();
546 if (nPointCount
==0) {
547 (*m_pEdgeTrack
)[0]=rPt
;
548 (*m_pEdgeTrack
)[1]=rPt
;
549 } else if (nPointCount
==1) {
550 if (!bTail1
) (*m_pEdgeTrack
)[1]=rPt
;
551 else { (*m_pEdgeTrack
)[1]=(*m_pEdgeTrack
)[0]; (*m_pEdgeTrack
)[0]=rPt
; }
553 if (!bTail1
) (*m_pEdgeTrack
)[sal_uInt16(nPointCount
-1)]=rPt
;
554 else (*m_pEdgeTrack
)[0]=rPt
;
556 ImpRecalcEdgeTrack();
557 SetBoundAndSnapRectsDirty();
560 void SdrEdgeObj::ImpDirtyEdgeTrack()
562 if ( !m_bEdgeTrackUserDefined
|| !getSdrModelFromSdrObject().isLocked() )
563 m_bEdgeTrackDirty
= true;
566 void SdrEdgeObj::ImpUndirtyEdgeTrack()
568 if (m_bEdgeTrackDirty
&& getSdrModelFromSdrObject().isLocked())
570 ImpRecalcEdgeTrack();
574 void SdrEdgeObj::ImpRecalcEdgeTrack()
576 // #i120437# if bEdgeTrackUserDefined, do not recalculate
577 if(m_bEdgeTrackUserDefined
)
582 // #i120437# also not when model locked during import, but remember
583 if(getSdrModelFromSdrObject().isLocked())
590 if(mbBoundRectCalculationRunning
)
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.
599 // To not run in a depth loop, use a coloring algorithm on
600 // SdrEdgeObj BoundRect calculations
601 mbBoundRectCalculationRunning
= true;
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
609 *m_pEdgeTrack
=ImpCalcEdgeTrack(*m_pEdgeTrack
,m_aCon1
,m_aCon2
,&m_aEdgeInfo
);
610 ImpSetAttrToEdgeInfo();
611 mbSuppressed
= false;
614 tools::Rectangle aBoundRect0
; if (m_pUserCall
!=nullptr) aBoundRect0
=GetCurrentBoundRect();
615 SetBoundAndSnapRectsDirty();
616 *m_pEdgeTrack
=ImpCalcEdgeTrack(*m_pEdgeTrack
,m_aCon1
,m_aCon2
,&m_aEdgeInfo
);
617 ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
618 m_bEdgeTrackDirty
=false;
620 // Only redraw here, no object change
623 SendUserCall(SdrUserCallType::Resize
,aBoundRect0
);
625 mbBoundRectCalculationRunning
= false;
629 SdrEscapeDirection
SdrEdgeObj::ImpCalcEscAngle(SdrObject
const * pObj
, const Point
& rPt
)
631 if (pObj
==nullptr) return SdrEscapeDirection::ALL
;
632 tools::Rectangle
aR(pObj
->GetSnapRect());
633 tools::Long dxl
=rPt
.X()-aR
.Left();
634 tools::Long dyo
=rPt
.Y()-aR
.Top();
635 tools::Long dxr
=aR
.Right()-rPt
.X();
636 tools::Long dyu
=aR
.Bottom()-rPt
.Y();
637 bool bxMitt
=std::abs(dxl
-dxr
)<2;
638 bool byMitt
=std::abs(dyo
-dyu
)<2;
639 tools::Long dx
=std::min(dxl
,dxr
);
640 tools::Long dy
=std::min(dyo
,dyu
);
641 bool bDiag
=std::abs(dx
-dy
)<2;
642 if (bxMitt
&& byMitt
) return SdrEscapeDirection::ALL
; // in the center
643 if (bDiag
) { // diagonally
644 SdrEscapeDirection nRet
=SdrEscapeDirection::SMART
;
645 if (byMitt
) nRet
|=SdrEscapeDirection::VERT
;
646 if (bxMitt
) nRet
|=SdrEscapeDirection::HORZ
;
647 if (dxl
<dxr
) { // left
648 if (dyo
<dyu
) nRet
|=SdrEscapeDirection::LEFT
| SdrEscapeDirection::TOP
;
649 else nRet
|=SdrEscapeDirection::LEFT
| SdrEscapeDirection::BOTTOM
;
651 if (dyo
<dyu
) nRet
|=SdrEscapeDirection::RIGHT
| SdrEscapeDirection::TOP
;
652 else nRet
|=SdrEscapeDirection::RIGHT
| SdrEscapeDirection::BOTTOM
;
656 if (dx
<dy
) { // horizontal
657 if (bxMitt
) return SdrEscapeDirection::HORZ
;
658 if (dxl
<dxr
) return SdrEscapeDirection::LEFT
;
659 else return SdrEscapeDirection::RIGHT
;
661 if (byMitt
) return SdrEscapeDirection::VERT
;
662 if (dyo
<dyu
) return SdrEscapeDirection::TOP
;
663 else return SdrEscapeDirection::BOTTOM
;
667 XPolygon
SdrEdgeObj::ImpCalcObjToCenter(const Point
& rStPt
, tools::Long nEscAngle
, const tools::Rectangle
& rRect
, const Point
& rMeeting
)
670 aXP
.Insert(XPOLY_APPEND
,rStPt
,PolyFlags::Normal
);
671 bool bRts
=nEscAngle
==0;
672 bool bObn
=nEscAngle
==9000;
673 bool bLks
=nEscAngle
==18000;
674 bool bUnt
=nEscAngle
==27000;
676 Point
aP1(rStPt
); // mandatory difference first,...
677 if (bLks
) aP1
.setX(rRect
.Left() );
678 if (bRts
) aP1
.setX(rRect
.Right() );
679 if (bObn
) aP1
.setY(rRect
.Top() );
680 if (bUnt
) aP1
.setY(rRect
.Bottom() );
682 Point
aP2(aP1
); // ...now increase to Meeting height, if necessary
683 if (bLks
&& rMeeting
.X()<=aP2
.X()) aP2
.setX(rMeeting
.X() );
684 if (bRts
&& rMeeting
.X()>=aP2
.X()) aP2
.setX(rMeeting
.X() );
685 if (bObn
&& rMeeting
.Y()<=aP2
.Y()) aP2
.setY(rMeeting
.Y() );
686 if (bUnt
&& rMeeting
.Y()>=aP2
.Y()) aP2
.setY(rMeeting
.Y() );
687 aXP
.Insert(XPOLY_APPEND
,aP2
,PolyFlags::Normal
);
690 if ((bLks
&& rMeeting
.X()>aP2
.X()) || (bRts
&& rMeeting
.X()<aP2
.X())) { // around
691 if (rMeeting
.Y()<aP2
.Y()) {
692 aP3
.setY(rRect
.Top() );
693 if (rMeeting
.Y()<aP3
.Y()) aP3
.setY(rMeeting
.Y() );
695 aP3
.setY(rRect
.Bottom() );
696 if (rMeeting
.Y()>aP3
.Y()) aP3
.setY(rMeeting
.Y() );
698 aXP
.Insert(XPOLY_APPEND
,aP3
,PolyFlags::Normal
);
699 if (aP3
.Y()!=rMeeting
.Y()) {
700 aP3
.setX(rMeeting
.X() );
701 aXP
.Insert(XPOLY_APPEND
,aP3
,PolyFlags::Normal
);
704 if ((bObn
&& rMeeting
.Y()>aP2
.Y()) || (bUnt
&& rMeeting
.Y()<aP2
.Y())) { // around
705 if (rMeeting
.X()<aP2
.X()) {
706 aP3
.setX(rRect
.Left() );
707 if (rMeeting
.X()<aP3
.X()) aP3
.setX(rMeeting
.X() );
709 aP3
.setX(rRect
.Right() );
710 if (rMeeting
.X()>aP3
.X()) aP3
.setX(rMeeting
.X() );
712 aXP
.Insert(XPOLY_APPEND
,aP3
,PolyFlags::Normal
);
713 if (aP3
.X()!=rMeeting
.X()) {
714 aP3
.setY(rMeeting
.Y() );
715 aXP
.Insert(XPOLY_APPEND
,aP3
,PolyFlags::Normal
);
719 if (aXP
.GetPointCount()>4) {
720 OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
726 XPolygon
SdrEdgeObj::ImpCalcEdgeTrack(const XPolygon
& rTrack0
, SdrObjConnection
& rCon1
, SdrObjConnection
& rCon2
, SdrEdgeInfoRec
* pInfo
) const
729 SdrGluePoint aGP1
,aGP2
;
730 SdrEscapeDirection nEsc1
=SdrEscapeDirection::ALL
,nEsc2
=SdrEscapeDirection::ALL
;
731 tools::Rectangle aBoundRect1
;
732 tools::Rectangle aBoundRect2
;
733 tools::Rectangle aBewareRect1
;
734 tools::Rectangle aBewareRect2
;
735 // first, get the old corner points
736 if (rTrack0
.GetPointCount()!=0) {
738 sal_uInt16 nSiz
=rTrack0
.GetPointCount();
744 auto aRectangle
= getOutRectangle();
745 if (!aRectangle
.IsEmpty()) {
746 aPt1
= aRectangle
.TopLeft();
747 aPt2
= aRectangle
.BottomRight();
751 // #i54102# To allow interactive preview, do also if not inserted
752 const bool bCon1(nullptr != rCon1
.m_pSdrObj
&& rCon1
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
753 const bool bCon2(nullptr != rCon2
.m_pSdrObj
&& rCon2
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
754 const SfxItemSet
& rSet
= GetObjectItemSet();
758 if (rCon1
.m_pSdrObj
==static_cast<SdrObject
const *>(this))
760 // check, just in case
761 aBoundRect1
= getOutRectangle();
765 if (getSdrModelFromSdrObject().GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect
))
766 aBoundRect1
= rCon1
.m_pSdrObj
->GetSnapRect();
768 aBoundRect1
= rCon1
.m_pSdrObj
->GetCurrentBoundRect();
771 aBoundRect1
.Move(rCon1
.m_aObjOfs
.X(),rCon1
.m_aObjOfs
.Y());
772 aBewareRect1
=aBoundRect1
;
773 sal_Int32 nH
= rSet
.Get(SDRATTR_EDGENODE1HORZDIST
).GetValue();
774 sal_Int32 nV
= rSet
.Get(SDRATTR_EDGENODE1VERTDIST
).GetValue();
775 aBewareRect1
.AdjustLeft( -nH
);
776 aBewareRect1
.AdjustRight(nH
);
777 aBewareRect1
.AdjustTop( -nV
);
778 aBewareRect1
.AdjustBottom(nV
);
782 aBoundRect1
=tools::Rectangle(aPt1
,aPt1
);
783 aBoundRect1
.Move(rCon1
.m_aObjOfs
.X(),rCon1
.m_aObjOfs
.Y());
784 aBewareRect1
=aBoundRect1
;
789 if (rCon2
.m_pSdrObj
==static_cast<SdrObject
const *>(this))
790 { // check, just in case
791 aBoundRect2
= getOutRectangle();
795 if (getSdrModelFromSdrObject().GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect
))
796 aBoundRect2
= rCon2
.m_pSdrObj
->GetSnapRect();
798 aBoundRect2
= rCon2
.m_pSdrObj
->GetCurrentBoundRect();
801 aBoundRect2
.Move(rCon2
.m_aObjOfs
.X(),rCon2
.m_aObjOfs
.Y());
802 aBewareRect2
=aBoundRect2
;
803 sal_Int32 nH
= rSet
.Get(SDRATTR_EDGENODE2HORZDIST
).GetValue();
804 sal_Int32 nV
= rSet
.Get(SDRATTR_EDGENODE2VERTDIST
).GetValue();
805 aBewareRect2
.AdjustLeft( -nH
);
806 aBewareRect2
.AdjustRight(nH
);
807 aBewareRect2
.AdjustTop( -nV
);
808 aBewareRect2
.AdjustBottom(nV
);
812 aBoundRect2
=tools::Rectangle(aPt2
,aPt2
);
813 aBoundRect2
.Move(rCon2
.m_aObjOfs
.X(),rCon2
.m_aObjOfs
.Y());
814 aBewareRect2
=aBoundRect2
;
818 sal_uIntPtr nBestQual
=0xFFFFFFFF;
819 SdrEdgeInfoRec aBestInfo
;
820 bool bAuto1
=bCon1
&& rCon1
.m_bBestVertex
;
821 bool bAuto2
=bCon2
&& rCon2
.m_bBestVertex
;
822 if (bAuto1
) rCon1
.m_bAutoVertex
=true;
823 if (bAuto2
) rCon2
.m_bAutoVertex
=true;
824 sal_uInt16 nBestAuto1
=0;
825 sal_uInt16 nBestAuto2
=0;
826 sal_uInt16 nCount1
=bAuto1
? 4 : 1;
827 sal_uInt16 nCount2
=bAuto2
? 4 : 1;
829 for (sal_uInt16 nNum1
=0; nNum1
<nCount1
; nNum1
++)
831 if (bAuto1
) rCon1
.m_nConId
=nNum1
;
832 if (bCon1
&& rCon1
.TakeGluePoint(aGP1
))
835 nEsc1
=aGP1
.GetEscDir();
836 if (nEsc1
==SdrEscapeDirection::SMART
) nEsc1
=ImpCalcEscAngle(rCon1
.m_pSdrObj
,aPt1
-rCon1
.m_aObjOfs
);
838 for (sal_uInt16 nNum2
=0; nNum2
<nCount2
; nNum2
++)
840 if (bAuto2
) rCon2
.m_nConId
=nNum2
;
841 if (bCon2
&& rCon2
.TakeGluePoint(aGP2
))
844 nEsc2
=aGP2
.GetEscDir();
845 if (nEsc2
==SdrEscapeDirection::SMART
) nEsc2
=ImpCalcEscAngle(rCon2
.m_pSdrObj
,aPt2
-rCon2
.m_aObjOfs
);
847 for (tools::Long nA1
=0; nA1
<36000; nA1
+=9000)
849 SdrEscapeDirection nE1
= nA1
==0 ? SdrEscapeDirection::RIGHT
: nA1
==9000 ? SdrEscapeDirection::TOP
: nA1
==18000 ? SdrEscapeDirection::LEFT
: nA1
==27000 ? SdrEscapeDirection::BOTTOM
: SdrEscapeDirection::SMART
;
850 for (tools::Long nA2
=0; nA2
<36000; nA2
+=9000)
852 SdrEscapeDirection nE2
= nA2
==0 ? SdrEscapeDirection::RIGHT
: nA2
==9000 ? SdrEscapeDirection::TOP
: nA2
==18000 ? SdrEscapeDirection::LEFT
: nA2
==27000 ? SdrEscapeDirection::BOTTOM
: SdrEscapeDirection::SMART
;
853 if ((nEsc1
&nE1
) && (nEsc2
&nE2
))
856 SdrEdgeInfoRec aInfo
;
857 if (pInfo
!=nullptr) aInfo
=*pInfo
;
858 XPolygon
aXP(ImpCalcEdgeTrack(aPt1
,nA1
,aBoundRect1
,aBewareRect1
,aPt2
,nA2
,aBoundRect2
,aBewareRect2
,&nQual
,&aInfo
));
861 aBestXP
=std::move(aXP
);
872 if (bAuto1
) rCon1
.m_nConId
=nBestAuto1
;
873 if (bAuto2
) rCon2
.m_nConId
=nBestAuto2
;
874 if (pInfo
!=nullptr) *pInfo
=aBestInfo
;
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_uIntPtr
* pnQuality
, SdrEdgeInfoRec
* pInfo
) const
882 SdrEdgeKind eKind
=GetObjectItem(SDRATTR_EDGEKIND
).GetValue();
883 bool bRts1
=nAngle1
==0;
884 bool bObn1
=nAngle1
==9000;
885 bool bLks1
=nAngle1
==18000;
886 bool bUnt1
=nAngle1
==27000;
887 bool bHor1
=bLks1
|| bRts1
;
888 bool bVer1
=bObn1
|| bUnt1
;
889 bool bRts2
=nAngle2
==0;
890 bool bObn2
=nAngle2
==9000;
891 bool bLks2
=nAngle2
==18000;
892 bool bUnt2
=nAngle2
==27000;
893 bool bHor2
=bLks2
|| bRts2
;
894 bool bVer2
=bObn2
|| bUnt2
;
896 pInfo
->m_nAngle1
=nAngle1
;
897 pInfo
->m_nAngle2
=nAngle2
;
898 pInfo
->m_nObj1Lines
=1;
899 pInfo
->m_nObj2Lines
=1;
900 pInfo
->m_nMiddleLine
=0xFFFF;
904 tools::Rectangle
aBoundRect1 (rBoundRect1
);
905 tools::Rectangle
aBoundRect2 (rBoundRect2
);
906 tools::Rectangle
aBewareRect1(rBewareRect1
);
907 tools::Rectangle
aBewareRect2(rBewareRect2
);
908 Point
aMeeting((aPt1
.X()+aPt2
.X()+1)/2,(aPt1
.Y()+aPt2
.Y()+1)/2);
909 if (eKind
==SdrEdgeKind::OneLine
) {
913 if (pnQuality
!=nullptr) {
914 *pnQuality
=std::abs(rPt1
.X()-rPt2
.X())+std::abs(rPt1
.Y()-rPt2
.Y());
917 } else if (eKind
==SdrEdgeKind::ThreeLines
) {
923 if (bRts1
) aXP
[1].setX(aBewareRect1
.Right() ); //+=500;
924 if (bObn1
) aXP
[1].setY(aBewareRect1
.Top() ); //-=500;
925 if (bLks1
) aXP
[1].setX(aBewareRect1
.Left() ); //-=500;
926 if (bUnt1
) aXP
[1].setY(aBewareRect1
.Bottom() ); //+=500;
927 if (bRts2
) aXP
[2].setX(aBewareRect2
.Right() ); //+=500;
928 if (bObn2
) aXP
[2].setY(aBewareRect2
.Top() ); //-=500;
929 if (bLks2
) aXP
[2].setX(aBewareRect2
.Left() ); //-=500;
930 if (bUnt2
) aXP
[2].setY(aBewareRect2
.Bottom() ); //+=500;
931 if (pnQuality
!=nullptr) {
932 tools::Long nQ
=std::abs(aXP
[1].X()-aXP
[0].X())+std::abs(aXP
[1].Y()-aXP
[0].Y());
933 nQ
+=std::abs(aXP
[2].X()-aXP
[1].X())+std::abs(aXP
[2].Y()-aXP
[1].Y());
934 nQ
+=std::abs(aXP
[3].X()-aXP
[2].X())+std::abs(aXP
[3].Y()-aXP
[2].Y());
938 pInfo
->m_nObj1Lines
=2;
939 pInfo
->m_nObj2Lines
=2;
941 aXP
[1].AdjustX(pInfo
->m_aObj1Line2
.X() );
943 aXP
[1].AdjustY(pInfo
->m_aObj1Line2
.Y() );
946 aXP
[2].AdjustX(pInfo
->m_aObj2Line2
.X() );
948 aXP
[2].AdjustY(pInfo
->m_aObj2Line2
.Y() );
953 sal_uInt16 nIntersections
=0;
955 Point
aC1(aBewareRect1
.Center());
956 Point
aC2(aBewareRect2
.Center());
957 if (aBewareRect1
.Left()<=aBewareRect2
.Right() && aBewareRect1
.Right()>=aBewareRect2
.Left()) {
958 // overlapping on the x axis
959 tools::Long n1
=std::max(aBewareRect1
.Left(),aBewareRect2
.Left());
960 tools::Long n2
=std::min(aBewareRect1
.Right(),aBewareRect2
.Right());
961 aMeeting
.setX((n1
+n2
+1)/2 );
963 // otherwise the center point of the empty space
964 if (aC1
.X()<aC2
.X()) {
965 aMeeting
.setX((aBewareRect1
.Right()+aBewareRect2
.Left()+1)/2 );
967 aMeeting
.setX((aBewareRect1
.Left()+aBewareRect2
.Right()+1)/2 );
970 if (aBewareRect1
.Top()<=aBewareRect2
.Bottom() && aBewareRect1
.Bottom()>=aBewareRect2
.Top()) {
971 // overlapping on the x axis
972 tools::Long n1
=std::max(aBewareRect1
.Top(),aBewareRect2
.Top());
973 tools::Long n2
=std::min(aBewareRect1
.Bottom(),aBewareRect2
.Bottom());
974 aMeeting
.setY((n1
+n2
+1)/2 );
976 // otherwise the center point of the empty space
977 if (aC1
.Y()<aC2
.Y()) {
978 aMeeting
.setY((aBewareRect1
.Bottom()+aBewareRect2
.Top()+1)/2 );
980 aMeeting
.setY((aBewareRect1
.Top()+aBewareRect2
.Bottom()+1)/2 );
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 tools::Long nXMin
=std::min(aBewareRect1
.Left(),aBewareRect2
.Left());
988 tools::Long nXMax
=std::max(aBewareRect1
.Right(),aBewareRect2
.Right());
989 tools::Long nYMin
=std::min(aBewareRect1
.Top(),aBewareRect2
.Top());
990 tools::Long nYMax
=std::max(aBewareRect1
.Bottom(),aBewareRect2
.Bottom());
991 bool bBewareOverlap
=aBewareRect1
.Right()>aBewareRect2
.Left() && aBewareRect1
.Left()<aBewareRect2
.Right() &&
992 aBewareRect1
.Bottom()>aBewareRect2
.Top() && aBewareRect1
.Top()<aBewareRect2
.Bottom();
993 unsigned nMainCase
=3;
994 if (nAngle1
==nAngle2
) nMainCase
=1;
995 else if ((bHor1
&& bHor2
) || (bVer1
&& bVer2
)) nMainCase
=2;
996 if (nMainCase
==1) { // case 1 (both go in one direction) is possible
997 if (bVer1
) aMeeting
.setX((aPt1
.X()+aPt2
.X()+1)/2 ); // Here, this is better than
998 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 bool bX1Ok
=aPt1
.X()<=aBewareRect2
.Left() || aPt1
.X()>=aBewareRect2
.Right();
1001 bool bX2Ok
=aPt2
.X()<=aBewareRect1
.Left() || aPt2
.X()>=aBewareRect1
.Right();
1002 bool bY1Ok
=aPt1
.Y()<=aBewareRect2
.Top() || aPt1
.Y()>=aBewareRect2
.Bottom();
1003 bool bY2Ok
=aPt2
.Y()<=aBewareRect1
.Top() || aPt2
.Y()>=aBewareRect1
.Bottom();
1004 if (bLks1
&& (bY1Ok
|| aBewareRect1
.Left()<aBewareRect2
.Right()) && (bY2Ok
|| aBewareRect2
.Left()<aBewareRect1
.Right())) {
1005 aMeeting
.setX(nXMin
);
1007 if (bRts1
&& (bY1Ok
|| aBewareRect1
.Right()>aBewareRect2
.Left()) && (bY2Ok
|| aBewareRect2
.Right()>aBewareRect1
.Left())) {
1008 aMeeting
.setX(nXMax
);
1010 if (bObn1
&& (bX1Ok
|| aBewareRect1
.Top()<aBewareRect2
.Bottom()) && (bX2Ok
|| aBewareRect2
.Top()<aBewareRect1
.Bottom())) {
1011 aMeeting
.setY(nYMin
);
1013 if (bUnt1
&& (bX1Ok
|| aBewareRect1
.Bottom()>aBewareRect2
.Top()) && (bX2Ok
|| aBewareRect2
.Bottom()>aBewareRect1
.Top())) {
1014 aMeeting
.setY(nYMax
);
1016 } else if (nMainCase
==2) {
1018 if (bHor1
) { // both horizontal
1020 (legend: line exits to the left (-|), right (|-))
1022 2.1: Facing; overlap only on y axis
1027 2.2, 2.3: Facing, offset vertically; no overlap on either
1033 2.4, 2.5: One below the other; overlap only on y axis
1038 2.6, 2.7: Not facing, offset vertically; no overlap on either
1044 2.8: Not facing; overlap only on y axis
1049 2.9: The objects's BewareRects overlap on x and y axis
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.
1058 // normalization; be aR1 the one exiting to the right,
1059 // be aR2 the one exiting to the left
1060 tools::Rectangle
aBewR1(bRts1
? aBewareRect1
: aBewareRect2
);
1061 tools::Rectangle
aBewR2(bRts1
? aBewareRect2
: aBewareRect1
);
1062 tools::Rectangle
aBndR1(bRts1
? aBoundRect1
: aBoundRect2
);
1063 tools::Rectangle
aBndR2(bRts1
? aBoundRect2
: aBoundRect1
);
1064 if (aBewR1
.Bottom()>aBewR2
.Top() && aBewR1
.Top()<aBewR2
.Bottom()) {
1065 // overlap on y axis; cases 2.1, 2.8, 2.9
1066 if (aBewR1
.Right()>aBewR2
.Left()) {
1068 Case 2.8: always going around on the outside
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).
1077 bool bCase29Direct
= false;
1078 bool bCase29
=aBewR1
.Right()>aBewR2
.Left();
1079 if (aBndR1
.Right()<=aBndR2
.Left()) { // case 2.9 without BoundRect overlap
1080 if ((aPt1
.Y()>aBewareRect2
.Top() && aPt1
.Y()<aBewareRect2
.Bottom()) ||
1081 (aPt2
.Y()>aBewareRect1
.Top() && aPt2
.Y()<aBewareRect1
.Bottom())) {
1082 bCase29Direct
= true;
1085 if (!bCase29Direct
) {
1086 bool bObenLang
=std::abs(nYMin
-aMeeting
.Y())<=std::abs(nYMax
-aMeeting
.Y());
1088 aMeeting
.setY(nYMin
);
1090 aMeeting
.setY(nYMax
);
1093 // now make sure that the surrounded object
1095 if ((aBewR1
.Center().Y()<aBewR2
.Center().Y()) != bObenLang
) {
1096 aMeeting
.setX(aBewR2
.Right() );
1098 aMeeting
.setX(aBewR1
.Left() );
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 tools::Long nWant1
=aBewR1
.Right()-aBndR1
.Right(); // distance at Obj1
1106 tools::Long nWant2
=aBndR2
.Left()-aBewR2
.Left(); // distance at Obj2
1107 tools::Long nSpace
=aBndR2
.Left()-aBndR1
.Right(); // available space
1108 tools::Long nGet1
=BigMulDiv(nWant1
,nSpace
,nWant1
+nWant2
);
1109 tools::Long nGet2
=nSpace
-nGet1
;
1110 if (bRts1
) { // revert normalization
1111 aBewareRect1
.AdjustRight(nGet1
-nWant1
);
1112 aBewareRect2
.AdjustLeft( -(nGet2
-nWant2
) );
1114 aBewareRect2
.AdjustRight(nGet1
-nWant1
);
1115 aBewareRect1
.AdjustLeft( -(nGet2
-nWant2
) );
1117 nIntersections
++; // lower quality
1121 } else if (bVer1
) { // both horizontal
1122 tools::Rectangle
aBewR1(bUnt1
? aBewareRect1
: aBewareRect2
);
1123 tools::Rectangle
aBewR2(bUnt1
? aBewareRect2
: aBewareRect1
);
1124 tools::Rectangle
aBndR1(bUnt1
? aBoundRect1
: aBoundRect2
);
1125 tools::Rectangle
aBndR2(bUnt1
? aBoundRect2
: aBoundRect1
);
1126 if (aBewR1
.Right()>aBewR2
.Left() && aBewR1
.Left()<aBewR2
.Right()) {
1127 // overlap on y axis; cases 2.1, 2.8, 2.9
1128 if (aBewR1
.Bottom()>aBewR2
.Top()) {
1130 Case 2.8 always going around on the outside (bDirect=false).
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).
1138 bool bCase29Direct
= false;
1139 bool bCase29
=aBewR1
.Bottom()>aBewR2
.Top();
1140 if (aBndR1
.Bottom()<=aBndR2
.Top()) { // case 2.9 without BoundRect overlap
1141 if ((aPt1
.X()>aBewareRect2
.Left() && aPt1
.X()<aBewareRect2
.Right()) ||
1142 (aPt2
.X()>aBewareRect1
.Left() && aPt2
.X()<aBewareRect1
.Right())) {
1143 bCase29Direct
= true;
1146 if (!bCase29Direct
) {
1147 bool bLinksLang
=std::abs(nXMin
-aMeeting
.X())<=std::abs(nXMax
-aMeeting
.X());
1149 aMeeting
.setX(nXMin
);
1151 aMeeting
.setX(nXMax
);
1154 // now make sure that the surrounded object
1156 if ((aBewR1
.Center().X()<aBewR2
.Center().X()) != bLinksLang
) {
1157 aMeeting
.setY(aBewR2
.Bottom() );
1159 aMeeting
.setY(aBewR1
.Top() );
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 tools::Long nWant1
=aBewR1
.Bottom()-aBndR1
.Bottom(); // difference at Obj1
1167 tools::Long nWant2
=aBndR2
.Top()-aBewR2
.Top(); // difference at Obj2
1168 tools::Long nSpace
=aBndR2
.Top()-aBndR1
.Bottom(); // available space
1169 tools::Long nGet1
=BigMulDiv(nWant1
,nSpace
,nWant1
+nWant2
);
1170 tools::Long nGet2
=nSpace
-nGet1
;
1171 if (bUnt1
) { // revert normalization
1172 aBewareRect1
.AdjustBottom(nGet1
-nWant1
);
1173 aBewareRect2
.AdjustTop( -(nGet2
-nWant2
) );
1175 aBewareRect2
.AdjustBottom(nGet1
-nWant1
);
1176 aBewareRect1
.AdjustTop( -(nGet2
-nWant2
) );
1178 nIntersections
++; // lower quality
1183 } else if (nMainCase
==3) { // case 3: one horizontal, the other vertical
1185 The line exits to the:
1194 * . * . * -- no overlap, at most might touch
1195 . . . . . -- overlap
1196 * . |- . * -- same height
1197 . . . . . -- overlap
1198 * . * . * -- no overlap, at most might touch
1200 Overall, there are 96 possible constellations, some of these can't even
1201 be unambiguously assigned to a certain case/method of handling.
1204 3.1: All those constellations that are covered reasonably well
1205 by the default MeetingPoint (20+12).
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: * . * _|_ * * . * . * * _|_ * . * * . * . *
1213 The last 16 of these cases can be excluded, if the objects face each other openly.
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: * . * _|_ * * _|_ * . * * . * . * * . * . *
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: * . * . * * . * . *
1233 tools::Rectangle
aTmpR1(aBewareRect1
);
1234 tools::Rectangle
aTmpR2(aBewareRect2
);
1235 if (bBewareOverlap
) {
1236 // overlapping BewareRects: use BoundRects for checking for case 3.2
1240 if ((((bRts1
&& aTmpR1
.Right ()<=aPt2
.X()) || (bLks1
&& aTmpR1
.Left()>=aPt2
.X())) &&
1241 ((bUnt2
&& aTmpR2
.Bottom()<=aPt1
.Y()) || (bObn2
&& aTmpR2
.Top ()>=aPt1
.Y()))) ||
1242 (((bRts2
&& aTmpR2
.Right ()<=aPt1
.X()) || (bLks2
&& aTmpR2
.Left()>=aPt1
.X())) &&
1243 ((bUnt1
&& aTmpR1
.Bottom()<=aPt2
.Y()) || (bObn1
&& aTmpR1
.Top ()>=aPt2
.Y())))) {
1244 // case 3.2 applies: connector with only 2 lines
1246 aMeeting
.setX(aPt2
.X() );
1247 aMeeting
.setY(aPt1
.Y() );
1249 aMeeting
.setX(aPt1
.X() );
1250 aMeeting
.setY(aPt2
.Y() );
1252 // in the case of overlapping BewareRects:
1253 aBewareRect1
=aTmpR1
;
1254 aBewareRect2
=aTmpR2
;
1255 } else if ((((bRts1
&& aBewareRect1
.Right ()>aBewareRect2
.Left ()) ||
1256 (bLks1
&& aBewareRect1
.Left ()<aBewareRect2
.Right ())) &&
1257 ((bUnt2
&& aBewareRect2
.Bottom()>aBewareRect1
.Top ()) ||
1258 (bObn2
&& aBewareRect2
.Top ()<aBewareRect1
.Bottom()))) ||
1259 (((bRts2
&& aBewareRect2
.Right ()>aBewareRect1
.Left ()) ||
1260 (bLks2
&& aBewareRect2
.Left ()<aBewareRect1
.Right ())) &&
1261 ((bUnt1
&& aBewareRect1
.Bottom()>aBewareRect2
.Top ()) ||
1262 (bObn1
&& aBewareRect1
.Top ()<aBewareRect2
.Bottom())))) {
1264 if (bRts1
|| bRts2
) { aMeeting
.setX(nXMax
); }
1265 if (bLks1
|| bLks2
) { aMeeting
.setX(nXMin
); }
1266 if (bUnt1
|| bUnt2
) { aMeeting
.setY(nYMax
); }
1267 if (bObn1
|| bObn2
) { aMeeting
.setY(nYMin
); }
1272 XPolygon
aXP1(ImpCalcObjToCenter(aPt1
,nAngle1
,aBewareRect1
,aMeeting
));
1273 XPolygon
aXP2(ImpCalcObjToCenter(aPt2
,nAngle2
,aBewareRect2
,aMeeting
));
1274 sal_uInt16 nXP1Cnt
=aXP1
.GetPointCount();
1275 sal_uInt16 nXP2Cnt
=aXP2
.GetPointCount();
1277 pInfo
->m_nObj1Lines
=nXP1Cnt
; if (nXP1Cnt
>1) pInfo
->m_nObj1Lines
--;
1278 pInfo
->m_nObj2Lines
=nXP2Cnt
; if (nXP2Cnt
>1) pInfo
->m_nObj2Lines
--;
1280 Point
aEP1(aXP1
[nXP1Cnt
-1]);
1281 Point
aEP2(aXP2
[nXP2Cnt
-1]);
1282 bool bInsMeetingPoint
=aEP1
.X()!=aEP2
.X() && aEP1
.Y()!=aEP2
.Y();
1283 bool bHorzE1
=aEP1
.Y()==aXP1
[nXP1Cnt
-2].Y(); // is last line of XP1 horizontal?
1284 bool bHorzE2
=aEP2
.Y()==aXP2
[nXP2Cnt
-2].Y(); // is last line of XP2 horizontal?
1285 if (aEP1
==aEP2
&& ((bHorzE1
&& bHorzE2
&& aEP1
.Y()==aEP2
.Y()) || (!bHorzE1
&& !bHorzE2
&& aEP1
.X()==aEP2
.X()))) {
1286 // special casing 'I' connectors
1287 nXP1Cnt
--; aXP1
.Remove(nXP1Cnt
,1);
1288 nXP2Cnt
--; aXP2
.Remove(nXP2Cnt
,1);
1290 if (bInsMeetingPoint
) {
1291 aXP1
.Insert(XPOLY_APPEND
,aMeeting
,PolyFlags::Normal
);
1293 // Inserting a MeetingPoint adds 2 new lines,
1294 // either might become the center line.
1295 if (pInfo
->m_nObj1Lines
==pInfo
->m_nObj2Lines
) {
1296 pInfo
->m_nObj1Lines
++;
1297 pInfo
->m_nObj2Lines
++;
1299 if (pInfo
->m_nObj1Lines
>pInfo
->m_nObj2Lines
) {
1300 pInfo
->m_nObj2Lines
++;
1301 pInfo
->m_nMiddleLine
=nXP1Cnt
-1;
1303 pInfo
->m_nObj1Lines
++;
1304 pInfo
->m_nMiddleLine
=nXP1Cnt
;
1308 } else if (pInfo
&& aEP1
!=aEP2
&& nXP1Cnt
+nXP2Cnt
>=4) {
1309 // By connecting both ends, another line is added, this becomes the center line.
1310 pInfo
->m_nMiddleLine
=nXP1Cnt
-1;
1312 sal_uInt16 nNum
=aXP2
.GetPointCount();
1313 if (aXP1
[nXP1Cnt
-1]==aXP2
[nXP2Cnt
-1] && nXP1Cnt
>1 && nXP2Cnt
>1) nNum
--;
1316 aXP1
.Insert(XPOLY_APPEND
,aXP2
[nNum
],PolyFlags::Normal
);
1318 sal_uInt16 nPointCount
=aXP1
.GetPointCount();
1320 if (pInfo
|| pnQuality
!=nullptr) {
1321 if (nPointCount
==2) cForm
='I';
1322 else if (nPointCount
==3) cForm
='L';
1323 else if (nPointCount
==4) { // Z or U
1324 if (nAngle1
==nAngle2
) cForm
='U';
1326 } else if (nPointCount
==6) { // S or C or ...
1327 if (nAngle1
!=nAngle2
) {
1328 // For type S, line 2 has the same direction as line 4.
1329 // For type C, the opposite is true.
1334 if (aP1
.Y()==aP2
.Y()) { // else both lines are horizontal
1335 if ((aP1
.X()<aP2
.X())==(aP3
.X()<aP4
.X())) cForm
='S';
1337 } else { // else both lines are vertical
1338 if ((aP1
.Y()<aP2
.Y())==(aP3
.Y()<aP4
.Y())) cForm
='S';
1341 } else cForm
='4'; // else is case 3 with 5 lines
1345 if (cForm
=='I' || cForm
=='L' || cForm
=='Z' || cForm
=='U') {
1346 pInfo
->m_nObj1Lines
=1;
1347 pInfo
->m_nObj2Lines
=1;
1348 if (cForm
=='Z' || cForm
=='U') {
1349 pInfo
->m_nMiddleLine
=1;
1351 pInfo
->m_nMiddleLine
=0xFFFF;
1353 } else if (cForm
=='S' || cForm
=='C') {
1354 pInfo
->m_nObj1Lines
=2;
1355 pInfo
->m_nObj2Lines
=2;
1356 pInfo
->m_nMiddleLine
=2;
1364 if (pnQuality
!=nullptr) {
1365 sal_uIntPtr nQual
=0;
1366 sal_uIntPtr nQual0
=nQual
; // prevent overruns
1367 bool bOverflow
= false;
1368 Point
aPt0(aXP1
[0]);
1369 for (sal_uInt16 nPntNum
=1; nPntNum
<nPointCount
; nPntNum
++) {
1370 Point
aPt1b(aXP1
[nPntNum
]);
1371 nQual
+=std::abs(aPt1b
.X()-aPt0
.X())+std::abs(aPt1b
.Y()-aPt0
.Y());
1372 if (nQual
<nQual0
) bOverflow
= true;
1377 sal_uInt16 nTmp
=nPointCount
;
1379 nTmp
=2; // Z shape with good quality (nTmp=2 instead of 4)
1380 sal_uIntPtr n1
=std::abs(aXP1
[1].X()-aXP1
[0].X())+std::abs(aXP1
[1].Y()-aXP1
[0].Y());
1381 sal_uIntPtr n2
=std::abs(aXP1
[2].X()-aXP1
[1].X())+std::abs(aXP1
[2].Y()-aXP1
[1].Y());
1382 sal_uIntPtr n3
=std::abs(aXP1
[3].X()-aXP1
[2].X())+std::abs(aXP1
[3].Y()-aXP1
[2].Y());
1383 // try to make lines lengths similar
1384 sal_uIntPtr nBesser
=0;
1387 if (n1
>=n2
) nBesser
=6;
1388 else if (n1
>=3*n3
) nBesser
=4;
1389 else if (n1
>=2*n3
) nBesser
=2;
1390 if (aXP1
[0].Y()!=aXP1
[1].Y()) nBesser
++; // vertical starting line gets a plus (for H/V-Prio)
1391 if (nQual
>nBesser
) nQual
-=nBesser
; else nQual
=0;
1395 nQual
+=static_cast<sal_uIntPtr
>(nTmp
)*0x01000000;
1396 if (nQual
<nQual0
|| nTmp
>15) bOverflow
= true;
1398 if (nPointCount
>=2) { // check exit angle again
1399 Point
aP1(aXP1
[1]); aP1
-=aXP1
[0];
1400 Point
aP2(aXP1
[nPointCount
-2]); aP2
-=aXP1
[nPointCount
-1];
1401 tools::Long nAng1
=0; if (aP1
.X()<0) nAng1
=18000; if (aP1
.Y()>0) nAng1
=27000;
1402 if (aP1
.Y()<0) nAng1
=9000;
1403 if (aP1
.X()!=0 && aP1
.Y()!=0) nAng1
=1; // slant?!
1404 tools::Long nAng2
=0; if (aP2
.X()<0) nAng2
=18000; if (aP2
.Y()>0) nAng2
=27000;
1405 if (aP2
.Y()<0) nAng2
=9000;
1406 if (aP2
.X()!=0 && aP2
.Y()!=0) nAng2
=1; // slant?!
1407 if (nAng1
!=nAngle1
) nIntersections
++;
1408 if (nAng2
!=nAngle2
) nIntersections
++;
1411 // For the quality check, use the original Rects and at the same time
1412 // check whether one them was scaled down for the calculation of the
1413 // Edges (e. g. case 2.9)
1414 aBewareRect1
=rBewareRect1
;
1415 aBewareRect2
=rBewareRect2
;
1417 for (sal_uInt16 i
=0; i
<nPointCount
; i
++) {
1418 Point
aPt1b(aXP1
[i
]);
1419 bool b1
=aPt1b
.X()>aBewareRect1
.Left() && aPt1b
.X()<aBewareRect1
.Right() &&
1420 aPt1b
.Y()>aBewareRect1
.Top() && aPt1b
.Y()<aBewareRect1
.Bottom();
1421 bool b2
=aPt1b
.X()>aBewareRect2
.Left() && aPt1b
.X()<aBewareRect2
.Right() &&
1422 aPt1b
.Y()>aBewareRect2
.Top() && aPt1b
.Y()<aBewareRect2
.Bottom();
1423 sal_uInt16 nInt0
=nIntersections
;
1424 if (i
==0 || i
==nPointCount
-1) {
1425 if (b1
&& b2
) nIntersections
++;
1427 if (b1
) nIntersections
++;
1428 if (b2
) nIntersections
++;
1430 // check for overlaps
1431 if (i
>0 && nInt0
==nIntersections
) {
1432 if (aPt0
.Y()==aPt1b
.Y()) { // horizontal line
1433 if (aPt0
.Y()>aBewareRect1
.Top() && aPt0
.Y()<aBewareRect1
.Bottom() &&
1434 ((aPt0
.X()<=aBewareRect1
.Left() && aPt1b
.X()>=aBewareRect1
.Right()) ||
1435 (aPt1b
.X()<=aBewareRect1
.Left() && aPt0
.X()>=aBewareRect1
.Right()))) nIntersections
++;
1436 if (aPt0
.Y()>aBewareRect2
.Top() && aPt0
.Y()<aBewareRect2
.Bottom() &&
1437 ((aPt0
.X()<=aBewareRect2
.Left() && aPt1b
.X()>=aBewareRect2
.Right()) ||
1438 (aPt1b
.X()<=aBewareRect2
.Left() && aPt0
.X()>=aBewareRect2
.Right()))) nIntersections
++;
1439 } else { // vertical line
1440 if (aPt0
.X()>aBewareRect1
.Left() && aPt0
.X()<aBewareRect1
.Right() &&
1441 ((aPt0
.Y()<=aBewareRect1
.Top() && aPt1b
.Y()>=aBewareRect1
.Bottom()) ||
1442 (aPt1b
.Y()<=aBewareRect1
.Top() && aPt0
.Y()>=aBewareRect1
.Bottom()))) nIntersections
++;
1443 if (aPt0
.X()>aBewareRect2
.Left() && aPt0
.X()<aBewareRect2
.Right() &&
1444 ((aPt0
.Y()<=aBewareRect2
.Top() && aPt1b
.Y()>=aBewareRect2
.Bottom()) ||
1445 (aPt1b
.Y()<=aBewareRect2
.Top() && aPt0
.Y()>=aBewareRect2
.Bottom()))) nIntersections
++;
1450 if (nPointCount
<=1) nIntersections
++;
1452 nQual
+=static_cast<sal_uIntPtr
>(nIntersections
)*0x10000000;
1453 if (nQual
<nQual0
|| nIntersections
>15) bOverflow
= true;
1455 if (bOverflow
|| nQual
==0xFFFFFFFF) nQual
=0xFFFFFFFE;
1458 if (pInfo
) { // now apply line offsets to aXP1
1459 if (pInfo
->m_nMiddleLine
!=0xFFFF) {
1460 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(SdrEdgeLineCode::MiddleLine
,aXP1
);
1461 if (pInfo
->ImpIsHorzLine(SdrEdgeLineCode::MiddleLine
,aXP1
)) {
1462 aXP1
[nIdx
].AdjustY(pInfo
->m_aMiddleLine
.Y() );
1463 aXP1
[nIdx
+1].AdjustY(pInfo
->m_aMiddleLine
.Y() );
1465 aXP1
[nIdx
].AdjustX(pInfo
->m_aMiddleLine
.X() );
1466 aXP1
[nIdx
+1].AdjustX(pInfo
->m_aMiddleLine
.X() );
1469 if (pInfo
->m_nObj1Lines
>=2) {
1470 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line2
,aXP1
);
1471 if (pInfo
->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line2
,aXP1
)) {
1472 aXP1
[nIdx
].AdjustY(pInfo
->m_aObj1Line2
.Y() );
1473 aXP1
[nIdx
+1].AdjustY(pInfo
->m_aObj1Line2
.Y() );
1475 aXP1
[nIdx
].AdjustX(pInfo
->m_aObj1Line2
.X() );
1476 aXP1
[nIdx
+1].AdjustX(pInfo
->m_aObj1Line2
.X() );
1479 if (pInfo
->m_nObj1Lines
>=3) {
1480 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line3
,aXP1
);
1481 if (pInfo
->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line3
,aXP1
)) {
1482 aXP1
[nIdx
].AdjustY(pInfo
->m_aObj1Line3
.Y() );
1483 aXP1
[nIdx
+1].AdjustY(pInfo
->m_aObj1Line3
.Y() );
1485 aXP1
[nIdx
].AdjustX(pInfo
->m_aObj1Line3
.X() );
1486 aXP1
[nIdx
+1].AdjustX(pInfo
->m_aObj1Line3
.X() );
1489 if (pInfo
->m_nObj2Lines
>=2) {
1490 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line2
,aXP1
);
1491 if (pInfo
->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line2
,aXP1
)) {
1492 aXP1
[nIdx
].AdjustY(pInfo
->m_aObj2Line2
.Y() );
1493 aXP1
[nIdx
+1].AdjustY(pInfo
->m_aObj2Line2
.Y() );
1495 aXP1
[nIdx
].AdjustX(pInfo
->m_aObj2Line2
.X() );
1496 aXP1
[nIdx
+1].AdjustX(pInfo
->m_aObj2Line2
.X() );
1499 if (pInfo
->m_nObj2Lines
>=3) {
1500 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line3
,aXP1
);
1501 if (pInfo
->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line3
,aXP1
)) {
1502 aXP1
[nIdx
].AdjustY(pInfo
->m_aObj2Line3
.Y() );
1503 aXP1
[nIdx
+1].AdjustY(pInfo
->m_aObj2Line3
.Y() );
1505 aXP1
[nIdx
].AdjustX(pInfo
->m_aObj2Line3
.X() );
1506 aXP1
[nIdx
+1].AdjustX(pInfo
->m_aObj2Line3
.X() );
1510 // make the connector a bezier curve, if appropriate
1511 if (eKind
!= SdrEdgeKind::Bezier
|| nPointCount
<= 2)
1514 if (pInfo
&& pInfo
->m_bUseOOXMLCurve
) // Routing method OOXML
1516 // The additional points needed are located on the segments of the path of the
1517 // corresponding bentConnector as calculated above.
1518 auto SegmentPoint
= [&aXP1
](const sal_uInt16
& nEnd
, const double& fFactor
) {
1520 aXP1
[nEnd
- 1].X() + FRound(fFactor
* (aXP1
[nEnd
].X() - aXP1
[nEnd
- 1].X())),
1521 aXP1
[nEnd
- 1].Y() + FRound(fFactor
* (aXP1
[nEnd
].Y() - aXP1
[nEnd
- 1].Y())));
1524 // We change the path going from end to start. Thus inserting points does not affect the index
1525 // of the preceding points.
1526 // The end point has index nPointCount-1 and is a normal point and kept.
1527 // Insert new control point in the middle of last segments.
1528 Point aControl
= SegmentPoint(nPointCount
- 1, 0.5);
1529 // Insert happens before specified index.
1530 aXP1
.Insert(nPointCount
- 1, aControl
, PolyFlags::Control
);
1531 for (sal_uInt16 nSegment
= nPointCount
- 2; nSegment
> 1; --nSegment
)
1533 // We need a normal point at center of segment and control points at 1/4 and 3/4 of
1534 // segment. At center and 1/4 are new points, at 3/4 will be replacement for the end
1535 // point of the segment.
1536 aControl
= SegmentPoint(nSegment
, 0.25);
1537 Point aNormal
= SegmentPoint(nSegment
, 0.5);
1538 aXP1
.SetFlags(nSegment
, PolyFlags::Control
);
1539 aXP1
[nSegment
] = SegmentPoint(nSegment
, 0.75);
1540 aXP1
.Insert(nSegment
, aNormal
, PolyFlags::Normal
);
1541 aXP1
.Insert(nSegment
, aControl
, PolyFlags::Control
);
1543 // The first segments needs a control point in the middle. It is replacement for the
1545 aXP1
.SetFlags(1, PolyFlags::Control
);
1546 aXP1
[1] = SegmentPoint(1, 0.5);
1548 else // Routing method LO
1550 Point
* pPt1
=&aXP1
[0];
1551 Point
* pPt2
=&aXP1
[1];
1552 Point
* pPt3
=&aXP1
[nPointCount
-2];
1553 Point
* pPt4
=&aXP1
[nPointCount
-1];
1554 tools::Long dx1
=pPt2
->X()-pPt1
->X();
1555 tools::Long dy1
=pPt2
->Y()-pPt1
->Y();
1556 tools::Long dx2
=pPt3
->X()-pPt4
->X();
1557 tools::Long dy2
=pPt3
->Y()-pPt4
->Y();
1558 if (cForm
=='L') { // nPointCount==3
1559 aXP1
.SetFlags(1,PolyFlags::Control
);
1561 aXP1
.Insert(2,aPt3
,PolyFlags::Control
);
1562 nPointCount
=aXP1
.GetPointCount();
1564 pPt3
=&aXP1
[nPointCount
-2];
1565 pPt2
->AdjustX( -(dx1
/3) );
1566 pPt2
->AdjustY( -(dy1
/3) );
1567 pPt3
->AdjustX( -(dx2
/3) );
1568 pPt3
->AdjustY( -(dy2
/3) );
1569 } else if (nPointCount
>=4 && nPointCount
<=6) { // Z or U or ...
1570 // To all others, the end points of the original lines become control
1571 // points for now. Thus, we need to do some more work for nPointCount>4!
1572 aXP1
.SetFlags(1,PolyFlags::Control
);
1573 aXP1
.SetFlags(nPointCount
-2,PolyFlags::Control
);
1575 pPt2
->AdjustX(dx1
/2 );
1576 pPt2
->AdjustY(dy1
/2 );
1577 pPt3
->AdjustX(dx2
/2 );
1578 pPt3
->AdjustY(dy2
/2 );
1579 if (nPointCount
==5) {
1580 // add a control point before and after center
1581 Point
aCenter(aXP1
[2]);
1582 tools::Long dx1b
=aCenter
.X()-aXP1
[1].X();
1583 tools::Long dy1b
=aCenter
.Y()-aXP1
[1].Y();
1584 tools::Long dx2b
=aCenter
.X()-aXP1
[3].X();
1585 tools::Long dy2b
=aCenter
.Y()-aXP1
[3].Y();
1586 aXP1
.Insert(2,aCenter
,PolyFlags::Control
);
1587 aXP1
.SetFlags(3,PolyFlags::Symmetric
);
1588 aXP1
.Insert(4,aCenter
,PolyFlags::Control
);
1589 aXP1
[2].AdjustX( -(dx1b
/2) );
1590 aXP1
[2].AdjustY( -(dy1b
/2) );
1591 aXP1
[3].AdjustX( -((dx1b
+dx2b
)/4) );
1592 aXP1
[3].AdjustY( -((dy1b
+dy2b
)/4) );
1593 aXP1
[4].AdjustX( -(dx2b
/2) );
1594 aXP1
[4].AdjustY( -(dy2b
/2) );
1596 if (nPointCount
==6) {
1597 Point
aPt1b(aXP1
[2]);
1598 Point
aPt2b(aXP1
[3]);
1599 aXP1
.Insert(2,aPt1b
,PolyFlags::Control
);
1600 aXP1
.Insert(5,aPt2b
,PolyFlags::Control
);
1601 tools::Long dx
=aPt1b
.X()-aPt2b
.X();
1602 tools::Long dy
=aPt1b
.Y()-aPt2b
.Y();
1603 aXP1
[3].AdjustX( -(dx
/2) );
1604 aXP1
[3].AdjustY( -(dy
/2) );
1605 aXP1
.SetFlags(3,PolyFlags::Symmetric
);
1606 aXP1
.Remove(4,1); // because it's identical with aXP1[3]
1614 There could be a maximum of 64 different developments with 5 lines, a
1615 maximum of 32 developments with 4 lines, a maximum of 16 developments with
1616 3 lines, a maximum of 8 developments with 2 lines.
1617 This gives us a total of 124 possibilities.
1618 Normalized for the 1st exit angle to the right, there remain 31 possibilities.
1619 Now, normalizing away the vertical mirroring, we get to a total of 16
1620 characteristic developments with 1 through 5 lines:
1622 1 line (type "I") --
1624 2 lines (type "L") __|
1626 3 lines (type "U") __ (type "Z") _
1629 4 lines #1 _| #2 | | #3 |_ #4 | |
1631 Of these, #1 is implausible, #2 is a rotated version of #3. This leaves
1632 #2 (from now on referred to as 4.1) and #4 (from now on referred to as 4.2).
1634 5 lines #1 _| #2 _| #3 ___ #4 _
1637 #5 |_ #6 |_ #7 _| | #8 ____
1639 Of these, 5.1, 5.2, 5.4 and 5.5 are implausible, 5.7 is a reversed version
1640 of 5.3. This leaves 5.3 (type "4"), 5.6 (type "S") and 5.8 (type "C").
1642 We now have discerned the 9 basic types to cover all 400 possible constellations
1643 of object positions and exit angles. 4 of the 9 types have got a center
1644 line (CL). The number of object margins per object varies between 0 and 3:
1651 4.2: y 0 1 = U+1, respectively 1+U
1652 4.4: n 0-2 0-2 = Z+1
1655 "C": n 0-3 0-3 = 1+U+1
1658 void SdrEdgeObj::Notify(SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
1660 const SfxHintId nId
= rHint
.GetId();
1661 bool bDataChg
=nId
==SfxHintId::DataChanged
;
1662 bool bDying
=nId
==SfxHintId::Dying
;
1663 bool bObj1
=m_aCon1
.m_pSdrObj
!=nullptr && m_aCon1
.m_pSdrObj
->GetBroadcaster()==&rBC
;
1664 bool bObj2
=m_aCon2
.m_pSdrObj
!=nullptr && m_aCon2
.m_pSdrObj
->GetBroadcaster()==&rBC
;
1665 if (bDying
&& (bObj1
|| bObj2
)) {
1666 // catch Dying, so AttrObj doesn't start broadcasting
1667 // about an alleged change of template
1668 if (bObj1
) m_aCon1
.m_pSdrObj
=nullptr;
1669 if (bObj2
) m_aCon2
.m_pSdrObj
=nullptr;
1672 if ( bObj1
|| bObj2
)
1674 m_bEdgeTrackUserDefined
= false;
1676 SdrTextObj::Notify(rBC
,rHint
);
1677 if (m_nNotifyingCount
!=0)return;
1680 m_nNotifyingCount
++;
1681 const SdrHint
* pSdrHint
= ( rHint
.GetId() == SfxHintId::ThisIsAnSdrHint
? static_cast<const SdrHint
*>(&rHint
) : nullptr );
1683 if (bDataChg
) { // StyleSheet changed
1684 ImpSetAttrToEdgeInfo(); // when changing templates, copy values from Pool to aEdgeInfo
1687 (bObj1
&& m_aCon1
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
1688 (bObj2
&& m_aCon2
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
1689 (pSdrHint
&& pSdrHint
->GetKind()==SdrHintKind::ObjectRemoved
))
1691 // broadcasting only, if on the same page
1692 tools::Rectangle aBoundRect0
; if (m_pUserCall
!=nullptr) aBoundRect0
=GetCurrentBoundRect();
1693 ImpDirtyEdgeTrack();
1695 // only redraw here, object hasn't actually changed
1698 SendUserCall(SdrUserCallType::Resize
,aBoundRect0
);
1700 m_nNotifyingCount
--;
1703 /** updates edges that are connected to the edges of this object
1704 as if the connected objects sent a repaint broadcast
1706 void SdrEdgeObj::Reformat()
1708 if( nullptr != m_aCon1
.m_pSdrObj
)
1710 SfxHint
aHint( SfxHintId::DataChanged
);
1711 Notify( *const_cast<SfxBroadcaster
*>(m_aCon1
.m_pSdrObj
->GetBroadcaster()), aHint
);
1714 if( nullptr != m_aCon2
.m_pSdrObj
)
1716 SfxHint
aHint( SfxHintId::DataChanged
);
1717 Notify( *const_cast<SfxBroadcaster
*>(m_aCon2
.m_pSdrObj
->GetBroadcaster()), aHint
);
1721 rtl::Reference
<SdrObject
> SdrEdgeObj::CloneSdrObject(SdrModel
& rTargetModel
) const
1723 return new SdrEdgeObj(rTargetModel
, *this);
1726 OUString
SdrEdgeObj::TakeObjNameSingul() const
1728 OUString
sName(SvxResId(STR_ObjNameSingulEDGE
));
1730 OUString
aName(GetName());
1731 if (!aName
.isEmpty())
1732 sName
+= " '" + aName
+ "'";
1736 OUString
SdrEdgeObj::TakeObjNamePlural() const
1738 return SvxResId(STR_ObjNamePluralEDGE
);
1741 basegfx::B2DPolyPolygon
SdrEdgeObj::TakeXorPoly() const
1743 basegfx::B2DPolyPolygon aPolyPolygon
;
1745 if (m_bEdgeTrackDirty
)
1747 const_cast<SdrEdgeObj
*>(this)->ImpRecalcEdgeTrack();
1752 aPolyPolygon
.append(m_pEdgeTrack
->getB2DPolygon());
1755 return aPolyPolygon
;
1758 void SdrEdgeObj::SetEdgeTrackPath( const basegfx::B2DPolyPolygon
& rPoly
)
1760 if ( !rPoly
.count() )
1762 m_bEdgeTrackDirty
= true;
1763 m_bEdgeTrackUserDefined
= false;
1767 *m_pEdgeTrack
= XPolygon( rPoly
.getB2DPolygon( 0 ) );
1768 m_bEdgeTrackDirty
= false;
1769 m_bEdgeTrackUserDefined
= true;
1771 // #i110629# also set aRect and maSnapeRect depending on pEdgeTrack
1772 const tools::Rectangle
aPolygonBounds(m_pEdgeTrack
->GetBoundRect());
1773 setRectangle(aPolygonBounds
);
1774 maSnapRect
= aPolygonBounds
;
1778 basegfx::B2DPolyPolygon
SdrEdgeObj::GetEdgeTrackPath() const
1780 basegfx::B2DPolyPolygon aPolyPolygon
;
1782 if (m_bEdgeTrackDirty
)
1783 const_cast<SdrEdgeObj
*>(this)->ImpRecalcEdgeTrack();
1785 aPolyPolygon
.append( m_pEdgeTrack
->getB2DPolygon() );
1787 return aPolyPolygon
;
1790 sal_uInt32
SdrEdgeObj::GetHdlCount() const
1792 SdrEdgeKind eKind
=GetObjectItem(SDRATTR_EDGEKIND
).GetValue();
1793 sal_uInt32
nHdlCnt(0);
1794 sal_uInt32
nPointCount(m_pEdgeTrack
->GetPointCount());
1799 if ((eKind
==SdrEdgeKind::OrthoLines
|| eKind
==SdrEdgeKind::Bezier
) && nPointCount
>= 4)
1801 sal_uInt32
nO1(m_aEdgeInfo
.m_nObj1Lines
> 0 ? m_aEdgeInfo
.m_nObj1Lines
- 1 : 0);
1802 sal_uInt32
nO2(m_aEdgeInfo
.m_nObj2Lines
> 0 ? m_aEdgeInfo
.m_nObj2Lines
- 1 : 0);
1803 sal_uInt32
nM(m_aEdgeInfo
.m_nMiddleLine
!= 0xFFFF ? 1 : 0);
1804 nHdlCnt
+= nO1
+ nO2
+ nM
;
1806 else if (eKind
==SdrEdgeKind::ThreeLines
&& nPointCount
== 4)
1808 if(GetConnectedNode(true))
1811 if(GetConnectedNode(false))
1819 void SdrEdgeObj::AddToHdlList(SdrHdlList
& rHdlList
) const
1821 sal_uInt32
nPointCount(m_pEdgeTrack
->GetPointCount());
1826 std::unique_ptr
<SdrHdl
> pHdl(new ImpEdgeHdl((*m_pEdgeTrack
)[0],SdrHdlKind::Poly
));
1827 if (m_aCon1
.m_pSdrObj
!=nullptr && m_aCon1
.m_bBestVertex
) pHdl
->Set1PixMore();
1828 pHdl
->SetPointNum(0);
1829 rHdlList
.AddHdl(std::move(pHdl
));
1832 std::unique_ptr
<SdrHdl
> pHdl(new ImpEdgeHdl((*m_pEdgeTrack
)[sal_uInt16(nPointCount
-1)],SdrHdlKind::Poly
));
1833 if (m_aCon2
.m_pSdrObj
!=nullptr && m_aCon2
.m_bBestVertex
) pHdl
->Set1PixMore();
1834 pHdl
->SetPointNum(1);
1835 rHdlList
.AddHdl(std::move(pHdl
));
1838 SdrEdgeKind eKind
=GetObjectItem(SDRATTR_EDGEKIND
).GetValue();
1839 if ((eKind
==SdrEdgeKind::OrthoLines
|| eKind
==SdrEdgeKind::Bezier
) && nPointCount
>= 4)
1841 sal_uInt32
nO1(m_aEdgeInfo
.m_nObj1Lines
> 0 ? m_aEdgeInfo
.m_nObj1Lines
- 1 : 0);
1842 sal_uInt32
nO2(m_aEdgeInfo
.m_nObj2Lines
> 0 ? m_aEdgeInfo
.m_nObj2Lines
- 1 : 0);
1843 sal_uInt32
nM(m_aEdgeInfo
.m_nMiddleLine
!= 0xFFFF ? 1 : 0);
1844 bool bOOXMLCurve
= m_aEdgeInfo
.m_bUseOOXMLCurve
&& eKind
== SdrEdgeKind::Bezier
;
1845 for(sal_uInt32 i
= 0; i
< (nO1
+ nO2
+ nM
); ++i
)
1848 sal_uInt32 nNum
= i
;
1849 std::unique_ptr
<ImpEdgeHdl
> pHdl(new ImpEdgeHdl(Point(),SdrHdlKind::Poly
));
1852 nPt
= bOOXMLCurve
? (nNum
+ 1) * 3 : nNum
+ 1;
1853 if (nNum
==0) pHdl
->SetLineCode(SdrEdgeLineCode::Obj1Line2
);
1854 if (nNum
==1) pHdl
->SetLineCode(SdrEdgeLineCode::Obj1Line3
);
1859 nPt
= bOOXMLCurve
? nPointCount
- 4 - nNum
* 3 : nPointCount
- 3 - nNum
;
1860 if (nNum
==0) pHdl
->SetLineCode(SdrEdgeLineCode::Obj2Line2
);
1861 if (nNum
==1) pHdl
->SetLineCode(SdrEdgeLineCode::Obj2Line3
);
1865 nPt
= bOOXMLCurve
? m_aEdgeInfo
.m_nMiddleLine
* 3
1866 : m_aEdgeInfo
.m_nMiddleLine
;
1867 pHdl
->SetLineCode(SdrEdgeLineCode::MiddleLine
);
1875 Point
aPos((*m_pEdgeTrack
)[static_cast<sal_uInt16
>(nPt
)]);
1880 Point
aPos((*m_pEdgeTrack
)[static_cast<sal_uInt16
>(nPt
)]);
1881 aPos
+=(*m_pEdgeTrack
)[static_cast<sal_uInt16
>(nPt
)+1];
1882 aPos
.setX( aPos
.X() / 2 );
1883 aPos
.setY( aPos
.Y() / 2 );
1886 pHdl
->SetPointNum(i
+ 2);
1887 rHdlList
.AddHdl(std::move(pHdl
));
1891 else if (eKind
==SdrEdgeKind::ThreeLines
&& nPointCount
== 4)
1893 if(GetConnectedNode(true))
1895 Point
aPos((*m_pEdgeTrack
)[1]);
1896 std::unique_ptr
<ImpEdgeHdl
> pHdl(new ImpEdgeHdl(aPos
,SdrHdlKind::Poly
));
1897 pHdl
->SetLineCode(SdrEdgeLineCode::Obj1Line2
);
1898 pHdl
->SetPointNum(2);
1899 rHdlList
.AddHdl(std::move(pHdl
));
1901 if(GetConnectedNode(false))
1903 Point
aPos((*m_pEdgeTrack
)[2]);
1904 std::unique_ptr
<ImpEdgeHdl
> pHdl(new ImpEdgeHdl(aPos
,SdrHdlKind::Poly
));
1905 pHdl
->SetLineCode(SdrEdgeLineCode::Obj2Line2
);
1906 pHdl
->SetPointNum(3);
1907 rHdlList
.AddHdl(std::move(pHdl
));
1913 bool SdrEdgeObj::hasSpecialDrag() const
1918 rtl::Reference
<SdrObject
> SdrEdgeObj::getFullDragClone() const
1920 // use Clone operator
1921 rtl::Reference
<SdrEdgeObj
> pRetval
= SdrObject::Clone(*this, getSdrModelFromSdrObject());
1923 // copy connections for clone, SdrEdgeObj::operator= does not do this
1924 pRetval
->ConnectToNode(true, GetConnectedNode(true));
1925 pRetval
->ConnectToNode(false, GetConnectedNode(false));
1930 bool SdrEdgeObj::beginSpecialDrag(SdrDragStat
& rDrag
) const
1935 rDrag
.SetEndDragChangesAttributes(true);
1937 if(rDrag
.GetHdl()->GetPointNum() < 2)
1945 bool SdrEdgeObj::applySpecialDrag(SdrDragStat
& rDragStat
)
1947 SdrEdgeObj
* pOriginalEdge
= dynamic_cast< SdrEdgeObj
* >(rDragStat
.GetHdl()->GetObj());
1948 const bool bOriginalEdgeModified(pOriginalEdge
== this);
1950 if(!bOriginalEdgeModified
&& pOriginalEdge
)
1952 // copy connections when clone is modified. This is needed because
1953 // as preparation to this modification the data from the original object
1954 // was copied to the clone using the operator=. As can be seen there,
1955 // that operator does not copy the connections (for good reason)
1956 ConnectToNode(true, pOriginalEdge
->GetConnection(true).GetSdrObject());
1957 ConnectToNode(false, pOriginalEdge
->GetConnection(false).GetSdrObject());
1960 if(rDragStat
.GetHdl()->GetPointNum() < 2)
1962 // start or end point connector drag
1963 const bool bDragA(0 == rDragStat
.GetHdl()->GetPointNum());
1964 const Point
aPointNow(rDragStat
.GetNow());
1966 rDragStat
.SetEndDragChangesGeoAndAttributes(true);
1968 if(rDragStat
.GetPageView())
1970 SdrObjConnection
* pDraggedOne(bDragA
? &m_aCon1
: &m_aCon2
);
1973 DisconnectFromNode(bDragA
);
1975 // look for new connection
1976 ImpFindConnector(aPointNow
, *rDragStat
.GetPageView(), *pDraggedOne
, pOriginalEdge
, nullptr, &rDragStat
);
1978 if(pDraggedOne
->m_pSdrObj
)
1980 // if found, officially connect to it; ImpFindConnector only
1982 SdrObject
* pNewConnection
= pDraggedOne
->m_pSdrObj
;
1983 pDraggedOne
->m_pSdrObj
= nullptr;
1984 ConnectToNode(bDragA
, pNewConnection
);
1987 if(rDragStat
.GetView() && !bOriginalEdgeModified
)
1989 // show IA helper, but only do this during IA, so not when the original
1990 // Edge gets modified in the last call
1991 rDragStat
.GetView()->SetConnectMarker(*pDraggedOne
);
1997 // change pEdgeTrack to modified position
2000 (*m_pEdgeTrack
)[0] = aPointNow
;
2004 (*m_pEdgeTrack
)[sal_uInt16(m_pEdgeTrack
->GetPointCount()-1)] = aPointNow
;
2008 // reset edge info's offsets, this is an end point drag
2009 m_aEdgeInfo
.m_aObj1Line2
= Point();
2010 m_aEdgeInfo
.m_aObj1Line3
= Point();
2011 m_aEdgeInfo
.m_aObj2Line2
= Point();
2012 m_aEdgeInfo
.m_aObj2Line3
= Point();
2013 m_aEdgeInfo
.m_aMiddleLine
= Point();
2017 // control point connector drag
2018 const ImpEdgeHdl
* pEdgeHdl
= static_cast<const ImpEdgeHdl
*>(rDragStat
.GetHdl());
2019 const SdrEdgeLineCode eLineCode
= pEdgeHdl
->GetLineCode();
2020 const Point
aDist(rDragStat
.GetNow() - rDragStat
.GetStart());
2021 sal_Int32
nDist(pEdgeHdl
->IsHorzDrag() ? aDist
.X() : aDist
.Y());
2023 nDist
+= m_aEdgeInfo
.ImpGetLineOffset(eLineCode
, *m_pEdgeTrack
);
2024 m_aEdgeInfo
.ImpSetLineOffset(eLineCode
, *m_pEdgeTrack
, nDist
);
2027 // force recalculation of EdgeTrack
2028 *m_pEdgeTrack
= ImpCalcEdgeTrack(*m_pEdgeTrack
, m_aCon1
, m_aCon2
, &m_aEdgeInfo
);
2029 m_bEdgeTrackDirty
=false;
2031 // save EdgeInfos and mark object as user modified
2032 ImpSetEdgeInfoToAttr();
2033 m_bEdgeTrackUserDefined
= false;
2035 SetBoundAndSnapRectsDirty();
2037 if(bOriginalEdgeModified
&& rDragStat
.GetView())
2039 // hide connect marker helper again when original gets changed.
2040 // This happens at the end of the interaction
2041 rDragStat
.GetView()->HideConnectMarker();
2047 OUString
SdrEdgeObj::getSpecialDragComment(const SdrDragStat
& rDrag
) const
2049 const bool bCreateComment(rDrag
.GetView() && this == rDrag
.GetView()->GetCreateObj());
2057 return ImpGetDescriptionStr(STR_DragEdgeTail
);
2062 basegfx::B2DPolygon
SdrEdgeObj::ImplAddConnectorOverlay(const SdrDragMethod
& rDragMethod
, bool bTail1
, bool bTail2
, bool bDetail
) const
2064 basegfx::B2DPolygon aResult
;
2068 SdrObjConnection
aMyCon1(m_aCon1
);
2069 SdrObjConnection
aMyCon2(m_aCon2
);
2073 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aMyCon1
.m_aObjOfs
.X(), aMyCon1
.m_aObjOfs
.Y()));
2074 aMyCon1
.m_aObjOfs
.setX( basegfx::fround(aTemp
.getX()) );
2075 aMyCon1
.m_aObjOfs
.setY( basegfx::fround(aTemp
.getY()) );
2080 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aMyCon2
.m_aObjOfs
.X(), aMyCon2
.m_aObjOfs
.Y()));
2081 aMyCon2
.m_aObjOfs
.setX( basegfx::fround(aTemp
.getX()) );
2082 aMyCon2
.m_aObjOfs
.setY( basegfx::fround(aTemp
.getY()) );
2085 SdrEdgeInfoRec
aInfo(m_aEdgeInfo
);
2086 XPolygon
aXP(ImpCalcEdgeTrack(*m_pEdgeTrack
, aMyCon1
, aMyCon2
, &aInfo
));
2088 if(aXP
.GetPointCount())
2090 aResult
= aXP
.getB2DPolygon();
2095 Point
aPt1((*m_pEdgeTrack
)[0]);
2096 Point
aPt2((*m_pEdgeTrack
)[sal_uInt16(m_pEdgeTrack
->GetPointCount() - 1)]);
2098 if (m_aCon1
.m_pSdrObj
&& (m_aCon1
.m_bBestConn
|| m_aCon1
.m_bBestVertex
))
2099 aPt1
= m_aCon1
.m_pSdrObj
->GetSnapRect().Center();
2101 if (m_aCon2
.m_pSdrObj
&& (m_aCon2
.m_bBestConn
|| m_aCon2
.m_bBestVertex
))
2102 aPt2
= m_aCon2
.m_pSdrObj
->GetSnapRect().Center();
2106 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aPt1
.X(), aPt1
.Y()));
2107 aPt1
.setX( basegfx::fround(aTemp
.getX()) );
2108 aPt1
.setY( basegfx::fround(aTemp
.getY()) );
2113 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aPt2
.X(), aPt2
.Y()));
2114 aPt2
.setX( basegfx::fround(aTemp
.getX()) );
2115 aPt2
.setY( basegfx::fround(aTemp
.getY()) );
2118 aResult
.append(basegfx::B2DPoint(aPt1
.X(), aPt1
.Y()));
2119 aResult
.append(basegfx::B2DPoint(aPt2
.X(), aPt2
.Y()));
2125 bool SdrEdgeObj::BegCreate(SdrDragStat
& rDragStat
)
2127 rDragStat
.SetNoSnap();
2128 m_pEdgeTrack
->SetPointCount(2);
2129 (*m_pEdgeTrack
)[0]=rDragStat
.GetStart();
2130 (*m_pEdgeTrack
)[1]=rDragStat
.GetNow();
2131 if (rDragStat
.GetPageView()!=nullptr) {
2132 ImpFindConnector(rDragStat
.GetStart(),*rDragStat
.GetPageView(),m_aCon1
,this);
2133 ConnectToNode(true,m_aCon1
.m_pSdrObj
);
2135 *m_pEdgeTrack
=ImpCalcEdgeTrack(*m_pEdgeTrack
,m_aCon1
,m_aCon2
,&m_aEdgeInfo
);
2139 bool SdrEdgeObj::MovCreate(SdrDragStat
& rDragStat
)
2141 sal_uInt16 nMax
=m_pEdgeTrack
->GetPointCount();
2142 (*m_pEdgeTrack
)[nMax
-1]=rDragStat
.GetNow();
2143 if (rDragStat
.GetPageView()!=nullptr) {
2144 ImpFindConnector(rDragStat
.GetNow(),*rDragStat
.GetPageView(),m_aCon2
,this);
2145 rDragStat
.GetView()->SetConnectMarker(m_aCon2
);
2147 SetBoundRectDirty();
2148 m_bSnapRectDirty
=true;
2149 ConnectToNode(false,m_aCon2
.m_pSdrObj
);
2150 *m_pEdgeTrack
=ImpCalcEdgeTrack(*m_pEdgeTrack
,m_aCon1
,m_aCon2
,&m_aEdgeInfo
);
2151 m_bEdgeTrackDirty
=false;
2155 bool SdrEdgeObj::EndCreate(SdrDragStat
& rDragStat
, SdrCreateCmd eCmd
)
2157 bool bOk
=(eCmd
==SdrCreateCmd::ForceEnd
|| rDragStat
.GetPointCount()>=2);
2159 ConnectToNode(true,m_aCon1
.m_pSdrObj
);
2160 ConnectToNode(false,m_aCon2
.m_pSdrObj
);
2161 if (rDragStat
.GetView()!=nullptr) {
2162 rDragStat
.GetView()->HideConnectMarker();
2164 ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
2166 SetBoundAndSnapRectsDirty();
2170 bool SdrEdgeObj::BckCreate(SdrDragStat
& rDragStat
)
2172 if (rDragStat
.GetView()!=nullptr) {
2173 rDragStat
.GetView()->HideConnectMarker();
2178 void SdrEdgeObj::BrkCreate(SdrDragStat
& rDragStat
)
2180 if (rDragStat
.GetView()!=nullptr) {
2181 rDragStat
.GetView()->HideConnectMarker();
2185 basegfx::B2DPolyPolygon
SdrEdgeObj::TakeCreatePoly(const SdrDragStat
& /*rStatDrag*/) const
2187 basegfx::B2DPolyPolygon aRetval
;
2188 aRetval
.append(m_pEdgeTrack
->getB2DPolygon());
2192 PointerStyle
SdrEdgeObj::GetCreatePointer() const
2194 return PointerStyle::DrawConnect
;
2197 bool SdrEdgeObj::ImpFindConnector(const Point
& rPt
, const SdrPageView
& rPV
, SdrObjConnection
& rCon
, const SdrEdgeObj
* pThis
, OutputDevice
* pOut
, SdrDragStat
* pDragStat
)
2200 if (pOut
==nullptr) pOut
=rPV
.GetView().GetFirstOutputDevice();
2201 if (pOut
==nullptr) return false;
2202 SdrObjList
* pOL
=rPV
.GetObjList();
2203 const SdrLayerIDSet
& rVisLayer
=rPV
.GetVisibleLayers();
2204 // sensitive area of connectors is twice as large as the one of the handles
2205 sal_uInt16 nMarkHdSiz
=rPV
.GetView().GetMarkHdlSizePixel();
2206 Size
aHalfConSiz(nMarkHdSiz
,nMarkHdSiz
);
2207 if (comphelper::LibreOfficeKit::isActive() && pOut
->GetMapMode().GetMapUnit() == MapUnit::Map100thMM
)
2208 aHalfConSiz
=pOut
->PixelToLogic(aHalfConSiz
, MapMode(MapUnit::Map100thMM
));
2210 aHalfConSiz
=pOut
->PixelToLogic(aHalfConSiz
);
2211 tools::Rectangle
aMouseRect(rPt
,rPt
);
2212 aMouseRect
.AdjustLeft( -(aHalfConSiz
.Width()) );
2213 aMouseRect
.AdjustTop( -(aHalfConSiz
.Height()) );
2214 aMouseRect
.AdjustRight(aHalfConSiz
.Width() );
2215 aMouseRect
.AdjustBottom(aHalfConSiz
.Height() );
2216 double fBoundHitTol
=static_cast<double>(aHalfConSiz
.Width())/2; if (fBoundHitTol
==0.0) fBoundHitTol
=1.0;
2217 size_t no
=pOL
->GetObjCount();
2219 SdrObjConnection aTestCon
;
2220 bool bTiledRendering
= comphelper::LibreOfficeKit::isActive();
2221 bool bHasRequestedOrdNum
= false;
2222 sal_Int32 requestedOrdNum
= -1;
2224 if (bTiledRendering
&& pDragStat
)
2226 auto& glueOptions
= pDragStat
->GetGlueOptions();
2227 if (glueOptions
.objectOrdNum
!= -1)
2229 requestedOrdNum
= glueOptions
.objectOrdNum
;
2230 bHasRequestedOrdNum
= true;
2234 while (no
>0 && !bFnd
) {
2235 // issue: group objects on different layers return LayerID=0!
2237 SdrObject
* pObj
=pOL
->GetObj(no
);
2238 if (bHasRequestedOrdNum
)
2240 if (pObj
->GetOrdNumDirect() != static_cast<sal_uInt32
>(requestedOrdNum
))
2243 if (rVisLayer
.IsSet(pObj
->GetLayer()) && pObj
->IsVisible() && // only visible objects
2244 (pThis
==nullptr || pObj
!=static_cast<SdrObject
const *>(pThis
))) // don't connect it to itself
2246 tools::Rectangle
aObjBound(pObj
->GetCurrentBoundRect());
2247 if (aObjBound
.Overlaps(aMouseRect
)) {
2248 aTestCon
.ResetVars();
2249 bool bEdge
=dynamic_cast<const SdrEdgeObj
*>(pObj
) != nullptr; // no BestCon for Edge
2250 // User-defined connectors have absolute priority.
2251 // After those come Vertex, Corner and center (Best), all prioritized equally.
2252 // Finally, a HitTest for the object.
2253 const SdrGluePointList
* pGPL
=pObj
->GetGluePointList();
2254 sal_uInt16 nGluePointCnt
=pGPL
==nullptr ? 0 : pGPL
->GetCount();
2255 sal_uInt16 nGesAnz
=nGluePointCnt
+9;
2256 bool bUserFnd
= false;
2257 sal_uIntPtr nBestDist
=0xFFFFFFFF;
2258 for (sal_uInt16 i
=0; i
<nGesAnz
; i
++)
2260 bool bUser
=i
<nGluePointCnt
;
2261 bool bVertex
=i
>=nGluePointCnt
+0 && i
<nGluePointCnt
+4;
2262 bool bCorner
=i
>=nGluePointCnt
+4 && i
<nGluePointCnt
+8;
2263 bool bCenter
=i
==nGluePointCnt
+8;
2266 sal_uInt16 nConNum
=i
;
2268 const SdrGluePoint
& rGP
=(*pGPL
)[nConNum
];
2269 aConPos
=rGP
.GetAbsolutePos(*pObj
);
2270 nConNum
=rGP
.GetId();
2272 } else if (bVertex
&& !bUserFnd
) {
2273 nConNum
=nConNum
-nGluePointCnt
;
2274 SdrGluePoint
aPt(pObj
->GetVertexGluePoint(nConNum
));
2275 aConPos
=aPt
.GetAbsolutePos(*pObj
);
2277 } else if (bCorner
&& !bUserFnd
) {
2278 nConNum
-=nGluePointCnt
+4;
2281 else if (bCenter
&& !bUserFnd
&& !bEdge
)
2283 // Suppress default connect at object center
2284 if(!pThis
|| !pThis
->GetSuppressDefaultConnect())
2288 aConPos
=aObjBound
.Center();
2292 if (bOk
&& aMouseRect
.Contains(aConPos
)) {
2293 if (bUser
) bUserFnd
= true;
2295 sal_uIntPtr nDist
=static_cast<sal_uIntPtr
>(std::abs(aConPos
.X()-rPt
.X()))+static_cast<sal_uIntPtr
>(std::abs(aConPos
.Y()-rPt
.Y()));
2296 if (nDist
<nBestDist
) {
2298 aTestCon
.m_pSdrObj
=pObj
;
2299 aTestCon
.m_nConId
=nConNum
;
2300 aTestCon
.m_bAutoCorner
=bCorner
;
2301 aTestCon
.m_bAutoVertex
=bVertex
;
2302 aTestCon
.m_bBestConn
=false; // bCenter;
2303 aTestCon
.m_bBestVertex
=bCenter
;
2307 // if no connector is hit, try HitTest again, for BestConnector (=bCenter)
2310 SdrObjectPrimitiveHit(*pObj
, rPt
, {fBoundHitTol
, fBoundHitTol
}, rPV
, &rVisLayer
, false))
2312 // Suppress default connect at object inside bound
2313 if(!pThis
|| !pThis
->GetSuppressDefaultConnect())
2316 aTestCon
.m_pSdrObj
=pObj
;
2317 aTestCon
.m_bBestConn
=true;
2321 aMouseRect
.AdjustLeft( -fBoundHitTol
);
2322 aMouseRect
.AdjustTop( -fBoundHitTol
);
2323 aMouseRect
.AdjustRight(fBoundHitTol
);
2324 aMouseRect
.AdjustBottom(fBoundHitTol
);
2334 void SdrEdgeObj::NbcSetSnapRect(const tools::Rectangle
& rRect
)
2336 const tools::Rectangle
aOld(GetSnapRect());
2341 if (getRectangle().IsEmpty() && 0 == m_pEdgeTrack
->GetPointCount())
2343 // #i110629# When initializing, do not scale on empty Rectangle; this
2344 // will mirror the underlying text object (!)
2345 setRectangle(rRect
);
2350 tools::Long nMulX
= rRect
.Right() - rRect
.Left();
2351 tools::Long nDivX
= aOld
.Right() - aOld
.Left();
2352 tools::Long nMulY
= rRect
.Bottom() - rRect
.Top();
2353 tools::Long nDivY
= aOld
.Bottom() - aOld
.Top();
2354 if ( nDivX
== 0 ) { nMulX
= 1; nDivX
= 1; }
2355 if ( nDivY
== 0 ) { nMulY
= 1; nDivY
= 1; }
2356 Fraction
aX(nMulX
, nDivX
);
2357 Fraction
aY(nMulY
, nDivY
);
2358 NbcResize(aOld
.TopLeft(), aX
, aY
);
2359 NbcMove(Size(rRect
.Left() - aOld
.Left(), rRect
.Top() - aOld
.Top()));
2363 void SdrEdgeObj::NbcMove(const Size
& rSiz
)
2365 SdrTextObj::NbcMove(rSiz
);
2366 MoveXPoly(*m_pEdgeTrack
,rSiz
);
2369 void SdrEdgeObj::NbcResize(const Point
& rRefPnt
, const Fraction
& aXFact
, const Fraction
& aYFact
)
2371 SdrTextObj::NbcResize(rRefPnt
,aXFact
,aXFact
);
2372 ResizeXPoly(*m_pEdgeTrack
,rRefPnt
,aXFact
,aYFact
);
2374 // if resize is not from paste, forget user distances
2375 if (!getSdrModelFromSdrObject().IsPasteResize())
2377 m_aEdgeInfo
.m_aObj1Line2
= Point();
2378 m_aEdgeInfo
.m_aObj1Line3
= Point();
2379 m_aEdgeInfo
.m_aObj2Line2
= Point();
2380 m_aEdgeInfo
.m_aObj2Line3
= Point();
2381 m_aEdgeInfo
.m_aMiddleLine
= Point();
2385 // #i54102# added rotation support
2386 void SdrEdgeObj::NbcRotate(const Point
& rRef
, Degree100 nAngle
, double sn
, double cs
)
2388 if(m_bEdgeTrackUserDefined
)
2390 // #i120437# special handling when track is imported, apply
2391 // transformation directly to imported track.
2392 SdrTextObj::NbcRotate(rRef
, nAngle
, sn
, cs
);
2393 RotateXPoly(*m_pEdgeTrack
, rRef
, sn
, cs
);
2397 // handle start and end point if not connected
2398 const bool bCon1(nullptr != m_aCon1
.m_pSdrObj
&& m_aCon1
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2399 const bool bCon2(nullptr != m_aCon2
.m_pSdrObj
&& m_aCon2
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2401 if(!bCon1
&& m_pEdgeTrack
)
2403 RotatePoint((*m_pEdgeTrack
)[0],rRef
,sn
,cs
);
2404 ImpDirtyEdgeTrack();
2407 if(!bCon2
&& m_pEdgeTrack
)
2409 sal_uInt16 nPointCount
= m_pEdgeTrack
->GetPointCount();
2410 RotatePoint((*m_pEdgeTrack
)[sal_uInt16(nPointCount
-1)],rRef
,sn
,cs
);
2411 ImpDirtyEdgeTrack();
2416 // #i54102# added mirror support
2417 void SdrEdgeObj::NbcMirror(const Point
& rRef1
, const Point
& rRef2
)
2419 if(m_bEdgeTrackUserDefined
)
2421 // #i120437# special handling when track is imported, apply
2422 // transformation directly to imported track.
2423 SdrTextObj::NbcMirror(rRef1
, rRef2
);
2424 MirrorXPoly(*m_pEdgeTrack
, rRef1
, rRef2
);
2428 // handle start and end point if not connected
2429 const bool bCon1(nullptr != m_aCon1
.m_pSdrObj
&& m_aCon1
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2430 const bool bCon2(nullptr != m_aCon2
.m_pSdrObj
&& m_aCon2
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2432 if(!bCon1
&& m_pEdgeTrack
)
2434 MirrorPoint((*m_pEdgeTrack
)[0],rRef1
,rRef2
);
2435 ImpDirtyEdgeTrack();
2438 if(!bCon2
&& m_pEdgeTrack
)
2440 sal_uInt16 nPointCount
= m_pEdgeTrack
->GetPointCount();
2441 MirrorPoint((*m_pEdgeTrack
)[sal_uInt16(nPointCount
-1)],rRef1
,rRef2
);
2442 ImpDirtyEdgeTrack();
2447 // #i54102# added shear support
2448 void SdrEdgeObj::NbcShear(const Point
& rRef
, Degree100 nAngle
, double tn
, bool bVShear
)
2450 if(m_bEdgeTrackUserDefined
)
2452 // #i120437# special handling when track is imported, apply
2453 // transformation directly to imported track.
2454 SdrTextObj::NbcShear(rRef
, nAngle
, tn
, bVShear
);
2455 ShearXPoly(*m_pEdgeTrack
, rRef
, tn
, bVShear
);
2459 // handle start and end point if not connected
2460 const bool bCon1(nullptr != m_aCon1
.m_pSdrObj
&& m_aCon1
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2461 const bool bCon2(nullptr != m_aCon2
.m_pSdrObj
&& m_aCon2
.m_pSdrObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2463 if(!bCon1
&& m_pEdgeTrack
)
2465 ShearPoint((*m_pEdgeTrack
)[0],rRef
,tn
,bVShear
);
2466 ImpDirtyEdgeTrack();
2469 if(!bCon2
&& m_pEdgeTrack
)
2471 sal_uInt16 nPointCount
= m_pEdgeTrack
->GetPointCount();
2472 ShearPoint((*m_pEdgeTrack
)[sal_uInt16(nPointCount
-1)],rRef
,tn
,bVShear
);
2473 ImpDirtyEdgeTrack();
2478 rtl::Reference
<SdrObject
> SdrEdgeObj::DoConvertToPolyObj(bool bBezier
, bool bAddText
) const
2480 basegfx::B2DPolyPolygon aPolyPolygon
;
2481 aPolyPolygon
.append(m_pEdgeTrack
->getB2DPolygon());
2482 rtl::Reference
<SdrObject
> pRet
= ImpConvertMakeObj(aPolyPolygon
, false, bBezier
);
2486 pRet
= ImpConvertAddText(std::move(pRet
), bBezier
);
2492 sal_uInt32
SdrEdgeObj::GetSnapPointCount() const
2497 Point
SdrEdgeObj::GetSnapPoint(sal_uInt32 i
) const
2499 const_cast<SdrEdgeObj
*>(this)->ImpUndirtyEdgeTrack();
2500 sal_uInt16 nCount
=m_pEdgeTrack
->GetPointCount();
2501 if (i
==0) return (*m_pEdgeTrack
)[0];
2502 else return (*m_pEdgeTrack
)[nCount
-1];
2505 bool SdrEdgeObj::IsPolyObj() const
2510 sal_uInt32
SdrEdgeObj::GetPointCount() const
2515 Point
SdrEdgeObj::GetPoint(sal_uInt32 i
) const
2517 const_cast<SdrEdgeObj
*>(this)->ImpUndirtyEdgeTrack();
2518 sal_uInt16 nCount
=m_pEdgeTrack
->GetPointCount();
2520 return (*m_pEdgeTrack
)[0];
2522 return (*m_pEdgeTrack
)[nCount
-1];
2525 void SdrEdgeObj::NbcSetPoint(const Point
& rPnt
, sal_uInt32 i
)
2527 // TODO: Need an implementation to connect differently.
2528 ImpUndirtyEdgeTrack();
2529 sal_uInt16 nCount
=m_pEdgeTrack
->GetPointCount();
2531 (*m_pEdgeTrack
)[0]=rPnt
;
2533 (*m_pEdgeTrack
)[nCount
-1]=rPnt
;
2534 SetEdgeTrackDirty();
2535 SetBoundAndSnapRectsDirty();
2538 SdrEdgeObjGeoData::SdrEdgeObjGeoData()
2539 : m_pEdgeTrack(std::in_place
)
2540 , m_bEdgeTrackDirty(false)
2541 , m_bEdgeTrackUserDefined(false)
2545 SdrEdgeObjGeoData::~SdrEdgeObjGeoData()
2549 std::unique_ptr
<SdrObjGeoData
> SdrEdgeObj::NewGeoData() const
2551 return std::make_unique
<SdrEdgeObjGeoData
>();
2554 void SdrEdgeObj::SaveGeoData(SdrObjGeoData
& rGeo
) const
2556 SdrTextObj::SaveGeoData(rGeo
);
2557 SdrEdgeObjGeoData
& rEGeo
=static_cast<SdrEdgeObjGeoData
&>(rGeo
);
2558 rEGeo
.m_aCon1
=m_aCon1
;
2559 rEGeo
.m_aCon2
=m_aCon2
;
2560 *rEGeo
.m_pEdgeTrack
=*m_pEdgeTrack
;
2561 rEGeo
.m_bEdgeTrackDirty
=m_bEdgeTrackDirty
;
2562 rEGeo
.m_bEdgeTrackUserDefined
=m_bEdgeTrackUserDefined
;
2563 rEGeo
.m_aEdgeInfo
=m_aEdgeInfo
;
2566 void SdrEdgeObj::RestoreGeoData(const SdrObjGeoData
& rGeo
)
2568 SdrTextObj::RestoreGeoData(rGeo
);
2569 const SdrEdgeObjGeoData
& rEGeo
=static_cast<const SdrEdgeObjGeoData
&>(rGeo
);
2570 if (m_aCon1
.m_pSdrObj
!=rEGeo
.m_aCon1
.m_pSdrObj
) {
2571 if (m_aCon1
.m_pSdrObj
!=nullptr) m_aCon1
.m_pSdrObj
->RemoveListener(*this);
2572 m_aCon1
=rEGeo
.m_aCon1
;
2573 if (m_aCon1
.m_pSdrObj
!=nullptr) m_aCon1
.m_pSdrObj
->AddListener(*this);
2576 m_aCon1
=rEGeo
.m_aCon1
;
2578 if (m_aCon2
.m_pSdrObj
!=rEGeo
.m_aCon2
.m_pSdrObj
) {
2579 if (m_aCon2
.m_pSdrObj
!=nullptr) m_aCon2
.m_pSdrObj
->RemoveListener(*this);
2580 m_aCon2
=rEGeo
.m_aCon2
;
2581 if (m_aCon2
.m_pSdrObj
!=nullptr) m_aCon2
.m_pSdrObj
->AddListener(*this);
2584 m_aCon2
=rEGeo
.m_aCon2
;
2586 *m_pEdgeTrack
=*rEGeo
.m_pEdgeTrack
;
2587 m_bEdgeTrackDirty
=rEGeo
.m_bEdgeTrackDirty
;
2588 m_bEdgeTrackUserDefined
=rEGeo
.m_bEdgeTrackUserDefined
;
2589 m_aEdgeInfo
=rEGeo
.m_aEdgeInfo
;
2592 Point
SdrEdgeObj::GetTailPoint( bool bTail
) const
2594 if( m_pEdgeTrack
&& m_pEdgeTrack
->GetPointCount()!=0)
2596 const XPolygon
& rTrack0
= *m_pEdgeTrack
;
2603 const sal_uInt16 nSiz
= rTrack0
.GetPointCount() - 1;
2604 return rTrack0
[nSiz
];
2610 return getOutRectangle().TopLeft();
2612 return getOutRectangle().BottomRight();
2617 void SdrEdgeObj::SetTailPoint( bool bTail
, const Point
& rPt
)
2619 ImpSetTailPoint( bTail
, rPt
);
2623 /** this method is used by the api to set a gluepoint for a connection
2624 nId == -1 : The best default point is automatically chosen
2625 0 <= nId <= 3 : One of the default points is chosen
2626 nId >= 4 : A user defined gluepoint is chosen
2628 void SdrEdgeObj::setGluePointIndex( bool bTail
, sal_Int32 nIndex
/* = -1 */ )
2630 SdrObjConnection
& rConn1
= GetConnection( bTail
);
2632 rConn1
.SetAutoVertex( nIndex
>= 0 && nIndex
<= 3 );
2633 rConn1
.SetBestConnection( nIndex
< 0 );
2634 rConn1
.SetBestVertex( nIndex
< 0 );
2638 nIndex
-= 3; // the start api index is 0, whereas the implementation in svx starts from 1
2640 // for user defined gluepoints we have
2641 // to get the id for this index first
2642 const SdrGluePointList
* pList
= rConn1
.GetSdrObject() ? rConn1
.GetSdrObject()->GetGluePointList() : nullptr;
2643 if( pList
== nullptr || SDRGLUEPOINT_NOTFOUND
== pList
->FindGluePoint(static_cast<sal_uInt16
>(nIndex
)) )
2646 else if( nIndex
< 0 )
2651 rConn1
.SetConnectorId( static_cast<sal_uInt16
>(nIndex
) );
2654 SetBoundAndSnapRectsDirty();
2655 ImpRecalcEdgeTrack();
2658 /** this method is used by the api to return a gluepoint id for a connection.
2659 See setGluePointId for possible return values */
2660 sal_Int32
SdrEdgeObj::getGluePointIndex( bool bTail
)
2662 SdrObjConnection
& rConn1
= GetConnection( bTail
);
2664 if( !rConn1
.IsBestConnection() )
2666 nId
= rConn1
.GetConnectorId();
2667 if( !rConn1
.IsAutoVertex() )
2668 nId
+= 3; // the start api index is 0, whereas the implementation in svx starts from 1
2673 // Implementation was missing; edge track needs to be invalidated additionally.
2674 void SdrEdgeObj::NbcSetAnchorPos(const Point
& rPnt
)
2676 // call parent functionality
2677 SdrTextObj::NbcSetAnchorPos(rPnt
);
2679 // Additionally, invalidate edge track
2680 ImpDirtyEdgeTrack();
2683 bool SdrEdgeObj::TRGetBaseGeometry(basegfx::B2DHomMatrix
& rMatrix
, basegfx::B2DPolyPolygon
& rPolyPolygon
) const
2685 // use base method from SdrObject, it's not rotatable and
2686 // a call to GetSnapRect() is used. That's what we need for Connector.
2687 return SdrObject::TRGetBaseGeometry(rMatrix
, rPolyPolygon
);
2690 void SdrEdgeObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::B2DPolyPolygon
& rPolyPolygon
)
2692 // where appropriate take care for existing connections. For now, just use the
2693 // implementation from SdrObject.
2694 SdrObject::TRSetBaseGeometry(rMatrix
, rPolyPolygon
);
2697 // for geometry access
2698 ::basegfx::B2DPolygon
SdrEdgeObj::getEdgeTrack() const
2700 if(m_bEdgeTrackDirty
)
2702 const_cast< SdrEdgeObj
* >(this)->ImpRecalcEdgeTrack();
2707 return m_pEdgeTrack
->getB2DPolygon();
2711 return ::basegfx::B2DPolygon();
2715 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */