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 (pObj
!=nullptr) { // one object has to be docked already!
64 rGP
=pObj
->GetVertexGluePoint(nConId
);
66 } else if (bAutoCorner
) {
67 rGP
=pObj
->GetCornerGluePoint(nConId
);
70 const SdrGluePointList
* pGPL
=pObj
->GetGluePointList();
72 sal_uInt16 nNum
=pGPL
->FindGluePoint(nConId
);
73 if (nNum
!=SDRGLUEPOINT_NOTFOUND
) {
81 Point
aPt(rGP
.GetAbsolutePos(*pObj
));
88 Point
& SdrEdgeInfoRec::ImpGetLineOffsetPoint(SdrEdgeLineCode eLineCode
)
91 case SdrEdgeLineCode::Obj1Line2
: return aObj1Line2
;
92 case SdrEdgeLineCode::Obj1Line3
: return aObj1Line3
;
93 case SdrEdgeLineCode::Obj2Line2
: return aObj2Line2
;
94 case SdrEdgeLineCode::Obj2Line3
: return aObj2Line3
;
95 case SdrEdgeLineCode::MiddleLine
: return 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 nMiddleLine
;
112 bool SdrEdgeInfoRec::ImpIsHorzLine(SdrEdgeLineCode eLineCode
, const XPolygon
& rXP
) const
114 sal_uInt16 nIdx
=ImpGetPolyIdx(eLineCode
,rXP
);
115 bool bHorz
=nAngle1
==0 || nAngle1
==18000;
116 if (eLineCode
==SdrEdgeLineCode::Obj2Line2
|| eLineCode
==SdrEdgeLineCode::Obj2Line3
) {
117 nIdx
=rXP
.GetPointCount()-nIdx
;
118 bHorz
=nAngle2
==0 || 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
),
160 bEdgeTrackDirty(false),
161 bEdgeTrackUserDefined(false),
162 // Default is to allow default connects
163 mbSuppressDefaultConnect(false),
164 mbBoundRectCalculationRunning(false),
169 pEdgeTrack
= XPolygon();
172 SdrEdgeObj::SdrEdgeObj(SdrModel
& rSdrModel
, SdrEdgeObj
const & rSource
)
173 : SdrTextObj(rSdrModel
, rSource
),
175 bEdgeTrackDirty(false),
176 bEdgeTrackUserDefined(false),
177 // Default is to allow default connects
178 mbSuppressDefaultConnect(false),
179 mbBoundRectCalculationRunning(false),
182 m_bClosedObj
= false;
184 pEdgeTrack
= rSource
.pEdgeTrack
;
185 bEdgeTrackDirty
=rSource
.bEdgeTrackDirty
;
186 aCon1
=rSource
.aCon1
;
187 aCon2
=rSource
.aCon2
;
190 aEdgeInfo
=rSource
.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).GetObject() || nullptr != GetConnection(false).GetObject())
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(aEdgeInfo
.nObj1Lines
>= 2 && n
< 3)
228 aEdgeInfo
.ImpSetLineOffset(SdrEdgeLineCode::Obj1Line2
, *pEdgeTrack
, nVals
[n
]);
232 if(aEdgeInfo
.nObj1Lines
>= 3 && n
< 3)
234 aEdgeInfo
.ImpSetLineOffset(SdrEdgeLineCode::Obj1Line3
, *pEdgeTrack
, nVals
[n
]);
238 if(aEdgeInfo
.nMiddleLine
!= 0xFFFF && n
< 3)
240 aEdgeInfo
.ImpSetLineOffset(SdrEdgeLineCode::MiddleLine
, *pEdgeTrack
, nVals
[n
]);
244 if(aEdgeInfo
.nObj2Lines
>= 3 && n
< 3)
246 aEdgeInfo
.ImpSetLineOffset(SdrEdgeLineCode::Obj2Line3
, *pEdgeTrack
, nVals
[n
]);
250 if(aEdgeInfo
.nObj2Lines
>= 2 && n
< 3)
252 aEdgeInfo
.ImpSetLineOffset(SdrEdgeLineCode::Obj2Line2
, *pEdgeTrack
, nVals
[n
]);
256 else if(eKind
== SdrEdgeKind::ThreeLines
)
258 bool bHor1
= aEdgeInfo
.nAngle1
== 0 || aEdgeInfo
.nAngle1
== 18000;
259 bool bHor2
= aEdgeInfo
.nAngle2
== 0 || aEdgeInfo
.nAngle2
== 18000;
263 aEdgeInfo
.aObj1Line2
.setX( nVal1
);
267 aEdgeInfo
.aObj1Line2
.setY( nVal1
);
272 aEdgeInfo
.aObj2Line2
.setX( nVal2
);
276 aEdgeInfo
.aObj2Line2
.setY( nVal2
);
283 void SdrEdgeObj::ImpSetEdgeInfoToAttr()
285 const SfxItemSet
& rSet
= GetObjectItemSet();
286 SdrEdgeKind eKind
= rSet
.Get(SDRATTR_EDGEKIND
).GetValue();
287 sal_Int32 nValCnt
= rSet
.Get(SDRATTR_EDGELINEDELTACOUNT
).GetValue();
288 sal_Int32 nVal1
= rSet
.Get(SDRATTR_EDGELINE1DELTA
).GetValue();
289 sal_Int32 nVal2
= rSet
.Get(SDRATTR_EDGELINE2DELTA
).GetValue();
290 sal_Int32 nVal3
= rSet
.Get(SDRATTR_EDGELINE3DELTA
).GetValue();
291 sal_Int32 nVals
[3] = { nVal1
, nVal2
, nVal3
};
294 if(eKind
== SdrEdgeKind::OrthoLines
|| eKind
== SdrEdgeKind::Bezier
)
296 if(aEdgeInfo
.nObj1Lines
>= 2 && n
< 3)
298 nVals
[n
] = aEdgeInfo
.ImpGetLineOffset(SdrEdgeLineCode::Obj1Line2
, *pEdgeTrack
);
302 if(aEdgeInfo
.nObj1Lines
>= 3 && n
< 3)
304 nVals
[n
] = aEdgeInfo
.ImpGetLineOffset(SdrEdgeLineCode::Obj1Line3
, *pEdgeTrack
);
308 if(aEdgeInfo
.nMiddleLine
!= 0xFFFF && n
< 3)
310 nVals
[n
] = aEdgeInfo
.ImpGetLineOffset(SdrEdgeLineCode::MiddleLine
, *pEdgeTrack
);
314 if(aEdgeInfo
.nObj2Lines
>= 3 && n
< 3)
316 nVals
[n
] = aEdgeInfo
.ImpGetLineOffset(SdrEdgeLineCode::Obj2Line3
, *pEdgeTrack
);
320 if(aEdgeInfo
.nObj2Lines
>= 2 && n
< 3)
322 nVals
[n
] = aEdgeInfo
.ImpGetLineOffset(SdrEdgeLineCode::Obj2Line2
, *pEdgeTrack
);
326 else if(eKind
== SdrEdgeKind::ThreeLines
)
328 bool bHor1
= aEdgeInfo
.nAngle1
== 0 || aEdgeInfo
.nAngle1
== 18000;
329 bool bHor2
= aEdgeInfo
.nAngle2
== 0 || aEdgeInfo
.nAngle2
== 18000;
332 nVals
[0] = bHor1
? aEdgeInfo
.aObj1Line2
.X() : aEdgeInfo
.aObj1Line2
.Y();
333 nVals
[1] = bHor2
? aEdgeInfo
.aObj2Line2
.X() : aEdgeInfo
.aObj2Line2
.Y();
336 if(!(n
!= nValCnt
|| nVals
[0] != nVal1
|| nVals
[1] != nVal2
|| nVals
[2] != nVal3
))
339 // Here no more notifying is necessary, just local changes are OK.
342 GetProperties().SetObjectItemDirect(SdrEdgeLineDeltaCountItem(n
));
345 if(nVals
[0] != nVal1
)
347 GetProperties().SetObjectItemDirect(makeSdrEdgeLine1DeltaItem(nVals
[0]));
350 if(nVals
[1] != nVal2
)
352 GetProperties().SetObjectItemDirect(makeSdrEdgeLine2DeltaItem(nVals
[1]));
355 if(nVals
[2] != nVal3
)
357 GetProperties().SetObjectItemDirect(makeSdrEdgeLine3DeltaItem(nVals
[2]));
362 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE3DELTA
);
367 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE2DELTA
);
372 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE1DELTA
);
376 void SdrEdgeObj::TakeObjInfo(SdrObjTransformInfoRec
& rInfo
) const
378 // #i54102# allow rotation, mirror and shear
379 rInfo
.bRotateFreeAllowed
= true;
380 rInfo
.bRotate90Allowed
= true;
381 rInfo
.bMirrorFreeAllowed
= true;
382 rInfo
.bMirror45Allowed
= true;
383 rInfo
.bMirror90Allowed
= true;
384 rInfo
.bTransparenceAllowed
= false;
385 rInfo
.bShearAllowed
= true;
386 rInfo
.bEdgeRadiusAllowed
= false;
387 bool bCanConv
=!HasText() || ImpCanConvTextToCurve();
388 rInfo
.bCanConvToPath
=bCanConv
;
389 rInfo
.bCanConvToPoly
=bCanConv
;
390 rInfo
.bCanConvToContour
= (rInfo
.bCanConvToPoly
|| LineGeometryUsageIsNecessary());
393 SdrObjKind
SdrEdgeObj::GetObjIdentifier() const
395 return SdrObjKind::Edge
;
398 const tools::Rectangle
& SdrEdgeObj::GetCurrentBoundRect() const
402 const_cast<SdrEdgeObj
*>(this)->ImpRecalcEdgeTrack();
405 return SdrTextObj::GetCurrentBoundRect();
408 const tools::Rectangle
& SdrEdgeObj::GetSnapRect() const
412 const_cast<SdrEdgeObj
*>(this)->ImpRecalcEdgeTrack();
415 return SdrTextObj::GetSnapRect();
418 void SdrEdgeObj::RecalcSnapRect()
420 maSnapRect
=pEdgeTrack
->GetBoundRect();
423 void SdrEdgeObj::TakeUnrotatedSnapRect(tools::Rectangle
& rRect
) const
428 SdrGluePoint
SdrEdgeObj::GetVertexGluePoint(sal_uInt16 nNum
) const
431 sal_uInt16 nPointCount
=pEdgeTrack
->GetPointCount();
434 Point aOfs
= GetSnapRect().Center();
435 if (nNum
==2 && GetConnectedNode(true)==nullptr) aPt
=(*pEdgeTrack
)[0];
436 else if (nNum
==3 && GetConnectedNode(false)==nullptr) aPt
=(*pEdgeTrack
)[nPointCount
-1];
438 if ((nPointCount
& 1) ==1) {
439 aPt
=(*pEdgeTrack
)[nPointCount
/2];
441 Point
aPt1((*pEdgeTrack
)[nPointCount
/2-1]);
442 Point
aPt2((*pEdgeTrack
)[nPointCount
/2]);
444 aPt1
.setX( aPt1
.X() / 2 );
445 aPt1
.setY( aPt1
.Y() / 2 );
451 SdrGluePoint
aGP(aPt
);
452 aGP
.SetPercent(false);
456 SdrGluePoint
SdrEdgeObj::GetCornerGluePoint(sal_uInt16 nNum
) const
458 return GetVertexGluePoint(nNum
);
461 const SdrGluePointList
* SdrEdgeObj::GetGluePointList() const
463 return nullptr; // no user defined gluepoints for connectors
466 SdrGluePointList
* SdrEdgeObj::ForceGluePointList()
468 return nullptr; // no user defined gluepoints for connectors
471 void SdrEdgeObj::ConnectToNode(bool bTail1
, SdrObject
* pObj
)
473 SdrObjConnection
& rCon
=GetConnection(bTail1
);
474 DisconnectFromNode(bTail1
);
476 pObj
->AddListener(*this);
479 // #i120437# If connection is set, reset bEdgeTrackUserDefined
480 bEdgeTrackUserDefined
= false;
486 void SdrEdgeObj::DisconnectFromNode(bool bTail1
)
488 SdrObjConnection
& rCon
=GetConnection(bTail1
);
489 if (rCon
.pObj
!=nullptr) {
490 rCon
.pObj
->RemoveListener(*this);
495 SdrObject
* SdrEdgeObj::GetConnectedNode(bool bTail1
) const
497 SdrObject
* pObj(GetConnection(bTail1
).pObj
);
500 && (pObj
->getSdrPageFromSdrObject() != getSdrPageFromSdrObject() || !pObj
->IsInserted()))
508 bool SdrEdgeObj::CheckNodeConnection(bool bTail1
) const
511 const SdrObjConnection
& rCon
=GetConnection(bTail1
);
512 sal_uInt16 nPointCount
=pEdgeTrack
->GetPointCount();
514 if(nullptr != rCon
.pObj
&& rCon
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject() && 0 != nPointCount
)
516 const SdrGluePointList
* pGPL
=rCon
.pObj
->GetGluePointList();
517 sal_uInt16 nGluePointCnt
=pGPL
==nullptr ? 0 : pGPL
->GetCount();
518 sal_uInt16 nGesAnz
=nGluePointCnt
+8;
519 Point
aTail(bTail1
? (*pEdgeTrack
)[0] : (*pEdgeTrack
)[sal_uInt16(nPointCount
-1)]);
520 for (sal_uInt16 i
=0; i
<nGesAnz
&& !bRet
; i
++) {
521 if (i
<nGluePointCnt
) { // UserDefined
522 bRet
=aTail
==(*pGPL
)[i
].GetAbsolutePos(*rCon
.pObj
);
523 } else if (i
<nGluePointCnt
+4) { // Vertex
524 SdrGluePoint
aPt(rCon
.pObj
->GetVertexGluePoint(i
-nGluePointCnt
));
525 bRet
=aTail
==aPt
.GetAbsolutePos(*rCon
.pObj
);
527 SdrGluePoint
aPt(rCon
.pObj
->GetCornerGluePoint(i
-nGluePointCnt
-4));
528 bRet
=aTail
==aPt
.GetAbsolutePos(*rCon
.pObj
);
535 void SdrEdgeObj::ImpSetTailPoint(bool bTail1
, const Point
& rPt
)
537 sal_uInt16 nPointCount
=pEdgeTrack
->GetPointCount();
538 if (nPointCount
==0) {
539 (*pEdgeTrack
)[0]=rPt
;
540 (*pEdgeTrack
)[1]=rPt
;
541 } else if (nPointCount
==1) {
542 if (!bTail1
) (*pEdgeTrack
)[1]=rPt
;
543 else { (*pEdgeTrack
)[1]=(*pEdgeTrack
)[0]; (*pEdgeTrack
)[0]=rPt
; }
545 if (!bTail1
) (*pEdgeTrack
)[sal_uInt16(nPointCount
-1)]=rPt
;
546 else (*pEdgeTrack
)[0]=rPt
;
548 ImpRecalcEdgeTrack();
549 SetBoundAndSnapRectsDirty();
552 void SdrEdgeObj::ImpDirtyEdgeTrack()
554 if ( !bEdgeTrackUserDefined
|| !getSdrModelFromSdrObject().isLocked() )
555 bEdgeTrackDirty
= true;
558 void SdrEdgeObj::ImpUndirtyEdgeTrack()
560 if (bEdgeTrackDirty
&& getSdrModelFromSdrObject().isLocked())
562 ImpRecalcEdgeTrack();
566 void SdrEdgeObj::ImpRecalcEdgeTrack()
568 // #i120437# if bEdgeTrackUserDefined, do not recalculate
569 if(bEdgeTrackUserDefined
)
574 // #i120437# also not when model locked during import, but remember
575 if(getSdrModelFromSdrObject().isLocked())
582 if(mbBoundRectCalculationRunning
)
584 // This object is involved into another ImpRecalcEdgeTrack() call
585 // from another SdrEdgeObj. Do not calculate again to avoid loop.
586 // Also, do not change bEdgeTrackDirty so that it gets recalculated
587 // later at the first non-looping call.
591 // To not run in a depth loop, use a coloring algorithm on
592 // SdrEdgeObj BoundRect calculations
593 mbBoundRectCalculationRunning
= true;
597 // #i123048# If layouting was ever suppressed, it needs to be done once
598 // and the attr need to be set at EdgeInfo, else these attr *will be lost*
599 // in the following call to ImpSetEdgeInfoToAttr() since they were never
601 *pEdgeTrack
=ImpCalcEdgeTrack(*pEdgeTrack
,aCon1
,aCon2
,&aEdgeInfo
);
602 ImpSetAttrToEdgeInfo();
603 mbSuppressed
= false;
606 tools::Rectangle aBoundRect0
; if (m_pUserCall
!=nullptr) aBoundRect0
=GetCurrentBoundRect();
607 SetBoundAndSnapRectsDirty();
608 *pEdgeTrack
=ImpCalcEdgeTrack(*pEdgeTrack
,aCon1
,aCon2
,&aEdgeInfo
);
609 ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
610 bEdgeTrackDirty
=false;
612 // Only redraw here, no object change
615 SendUserCall(SdrUserCallType::Resize
,aBoundRect0
);
617 mbBoundRectCalculationRunning
= false;
621 SdrEscapeDirection
SdrEdgeObj::ImpCalcEscAngle(SdrObject
const * pObj
, const Point
& rPt
)
623 if (pObj
==nullptr) return SdrEscapeDirection::ALL
;
624 tools::Rectangle
aR(pObj
->GetSnapRect());
625 tools::Long dxl
=rPt
.X()-aR
.Left();
626 tools::Long dyo
=rPt
.Y()-aR
.Top();
627 tools::Long dxr
=aR
.Right()-rPt
.X();
628 tools::Long dyu
=aR
.Bottom()-rPt
.Y();
629 bool bxMitt
=std::abs(dxl
-dxr
)<2;
630 bool byMitt
=std::abs(dyo
-dyu
)<2;
631 tools::Long dx
=std::min(dxl
,dxr
);
632 tools::Long dy
=std::min(dyo
,dyu
);
633 bool bDiag
=std::abs(dx
-dy
)<2;
634 if (bxMitt
&& byMitt
) return SdrEscapeDirection::ALL
; // in the center
635 if (bDiag
) { // diagonally
636 SdrEscapeDirection nRet
=SdrEscapeDirection::SMART
;
637 if (byMitt
) nRet
|=SdrEscapeDirection::VERT
;
638 if (bxMitt
) nRet
|=SdrEscapeDirection::HORZ
;
639 if (dxl
<dxr
) { // left
640 if (dyo
<dyu
) nRet
|=SdrEscapeDirection::LEFT
| SdrEscapeDirection::TOP
;
641 else nRet
|=SdrEscapeDirection::LEFT
| SdrEscapeDirection::BOTTOM
;
643 if (dyo
<dyu
) nRet
|=SdrEscapeDirection::RIGHT
| SdrEscapeDirection::TOP
;
644 else nRet
|=SdrEscapeDirection::RIGHT
| SdrEscapeDirection::BOTTOM
;
648 if (dx
<dy
) { // horizontal
649 if (bxMitt
) return SdrEscapeDirection::HORZ
;
650 if (dxl
<dxr
) return SdrEscapeDirection::LEFT
;
651 else return SdrEscapeDirection::RIGHT
;
653 if (byMitt
) return SdrEscapeDirection::VERT
;
654 if (dyo
<dyu
) return SdrEscapeDirection::TOP
;
655 else return SdrEscapeDirection::BOTTOM
;
659 XPolygon
SdrEdgeObj::ImpCalcObjToCenter(const Point
& rStPt
, tools::Long nEscAngle
, const tools::Rectangle
& rRect
, const Point
& rMeeting
)
662 aXP
.Insert(XPOLY_APPEND
,rStPt
,PolyFlags::Normal
);
663 bool bRts
=nEscAngle
==0;
664 bool bObn
=nEscAngle
==9000;
665 bool bLks
=nEscAngle
==18000;
666 bool bUnt
=nEscAngle
==27000;
668 Point
aP1(rStPt
); // mandatory difference first,...
669 if (bLks
) aP1
.setX(rRect
.Left() );
670 if (bRts
) aP1
.setX(rRect
.Right() );
671 if (bObn
) aP1
.setY(rRect
.Top() );
672 if (bUnt
) aP1
.setY(rRect
.Bottom() );
674 Point
aP2(aP1
); // ...now increase to Meeting height, if necessary
675 if (bLks
&& rMeeting
.X()<=aP2
.X()) aP2
.setX(rMeeting
.X() );
676 if (bRts
&& rMeeting
.X()>=aP2
.X()) aP2
.setX(rMeeting
.X() );
677 if (bObn
&& rMeeting
.Y()<=aP2
.Y()) aP2
.setY(rMeeting
.Y() );
678 if (bUnt
&& rMeeting
.Y()>=aP2
.Y()) aP2
.setY(rMeeting
.Y() );
679 aXP
.Insert(XPOLY_APPEND
,aP2
,PolyFlags::Normal
);
682 if ((bLks
&& rMeeting
.X()>aP2
.X()) || (bRts
&& rMeeting
.X()<aP2
.X())) { // around
683 if (rMeeting
.Y()<aP2
.Y()) {
684 aP3
.setY(rRect
.Top() );
685 if (rMeeting
.Y()<aP3
.Y()) aP3
.setY(rMeeting
.Y() );
687 aP3
.setY(rRect
.Bottom() );
688 if (rMeeting
.Y()>aP3
.Y()) aP3
.setY(rMeeting
.Y() );
690 aXP
.Insert(XPOLY_APPEND
,aP3
,PolyFlags::Normal
);
691 if (aP3
.Y()!=rMeeting
.Y()) {
692 aP3
.setX(rMeeting
.X() );
693 aXP
.Insert(XPOLY_APPEND
,aP3
,PolyFlags::Normal
);
696 if ((bObn
&& rMeeting
.Y()>aP2
.Y()) || (bUnt
&& rMeeting
.Y()<aP2
.Y())) { // around
697 if (rMeeting
.X()<aP2
.X()) {
698 aP3
.setX(rRect
.Left() );
699 if (rMeeting
.X()<aP3
.X()) aP3
.setX(rMeeting
.X() );
701 aP3
.setX(rRect
.Right() );
702 if (rMeeting
.X()>aP3
.X()) aP3
.setX(rMeeting
.X() );
704 aXP
.Insert(XPOLY_APPEND
,aP3
,PolyFlags::Normal
);
705 if (aP3
.X()!=rMeeting
.X()) {
706 aP3
.setY(rMeeting
.Y() );
707 aXP
.Insert(XPOLY_APPEND
,aP3
,PolyFlags::Normal
);
711 if (aXP
.GetPointCount()>4) {
712 OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
718 XPolygon
SdrEdgeObj::ImpCalcEdgeTrack(const XPolygon
& rTrack0
, SdrObjConnection
& rCon1
, SdrObjConnection
& rCon2
, SdrEdgeInfoRec
* pInfo
) const
721 SdrGluePoint aGP1
,aGP2
;
722 SdrEscapeDirection nEsc1
=SdrEscapeDirection::ALL
,nEsc2
=SdrEscapeDirection::ALL
;
723 tools::Rectangle aBoundRect1
;
724 tools::Rectangle aBoundRect2
;
725 tools::Rectangle aBewareRect1
;
726 tools::Rectangle aBewareRect2
;
727 // first, get the old corner points
728 if (rTrack0
.GetPointCount()!=0) {
730 sal_uInt16 nSiz
=rTrack0
.GetPointCount();
736 auto aRectangle
= getOutRectangle();
737 if (!aRectangle
.IsEmpty()) {
738 aPt1
= aRectangle
.TopLeft();
739 aPt2
= aRectangle
.BottomRight();
743 // #i54102# To allow interactive preview, do also if not inserted
744 const bool bCon1(nullptr != rCon1
.pObj
&& rCon1
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
745 const bool bCon2(nullptr != rCon2
.pObj
&& rCon2
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
746 const SfxItemSet
& rSet
= GetObjectItemSet();
750 if (rCon1
.pObj
==static_cast<SdrObject
const *>(this))
752 // check, just in case
753 aBoundRect1
= getOutRectangle();
757 if (getSdrModelFromSdrObject().GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect
))
758 aBoundRect1
= rCon1
.pObj
->GetSnapRect();
760 aBoundRect1
= rCon1
.pObj
->GetCurrentBoundRect();
763 aBoundRect1
.Move(rCon1
.aObjOfs
.X(),rCon1
.aObjOfs
.Y());
764 aBewareRect1
=aBoundRect1
;
765 sal_Int32 nH
= rSet
.Get(SDRATTR_EDGENODE1HORZDIST
).GetValue();
766 sal_Int32 nV
= rSet
.Get(SDRATTR_EDGENODE1VERTDIST
).GetValue();
767 aBewareRect1
.AdjustLeft( -nH
);
768 aBewareRect1
.AdjustRight(nH
);
769 aBewareRect1
.AdjustTop( -nV
);
770 aBewareRect1
.AdjustBottom(nV
);
774 aBoundRect1
=tools::Rectangle(aPt1
,aPt1
);
775 aBoundRect1
.Move(rCon1
.aObjOfs
.X(),rCon1
.aObjOfs
.Y());
776 aBewareRect1
=aBoundRect1
;
781 if (rCon2
.pObj
==static_cast<SdrObject
const *>(this))
782 { // check, just in case
783 aBoundRect2
= getOutRectangle();
787 if (getSdrModelFromSdrObject().GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect
))
788 aBoundRect2
= rCon2
.pObj
->GetSnapRect();
790 aBoundRect2
= rCon2
.pObj
->GetCurrentBoundRect();
793 aBoundRect2
.Move(rCon2
.aObjOfs
.X(),rCon2
.aObjOfs
.Y());
794 aBewareRect2
=aBoundRect2
;
795 sal_Int32 nH
= rSet
.Get(SDRATTR_EDGENODE2HORZDIST
).GetValue();
796 sal_Int32 nV
= rSet
.Get(SDRATTR_EDGENODE2VERTDIST
).GetValue();
797 aBewareRect2
.AdjustLeft( -nH
);
798 aBewareRect2
.AdjustRight(nH
);
799 aBewareRect2
.AdjustTop( -nV
);
800 aBewareRect2
.AdjustBottom(nV
);
804 aBoundRect2
=tools::Rectangle(aPt2
,aPt2
);
805 aBoundRect2
.Move(rCon2
.aObjOfs
.X(),rCon2
.aObjOfs
.Y());
806 aBewareRect2
=aBoundRect2
;
810 sal_uIntPtr nBestQual
=0xFFFFFFFF;
811 SdrEdgeInfoRec aBestInfo
;
812 bool bAuto1
=bCon1
&& rCon1
.bBestVertex
;
813 bool bAuto2
=bCon2
&& rCon2
.bBestVertex
;
814 if (bAuto1
) rCon1
.bAutoVertex
=true;
815 if (bAuto2
) rCon2
.bAutoVertex
=true;
816 sal_uInt16 nBestAuto1
=0;
817 sal_uInt16 nBestAuto2
=0;
818 sal_uInt16 nCount1
=bAuto1
? 4 : 1;
819 sal_uInt16 nCount2
=bAuto2
? 4 : 1;
821 for (sal_uInt16 nNum1
=0; nNum1
<nCount1
; nNum1
++)
823 if (bAuto1
) rCon1
.nConId
=nNum1
;
824 if (bCon1
&& rCon1
.TakeGluePoint(aGP1
))
827 nEsc1
=aGP1
.GetEscDir();
828 if (nEsc1
==SdrEscapeDirection::SMART
) nEsc1
=ImpCalcEscAngle(rCon1
.pObj
,aPt1
-rCon1
.aObjOfs
);
830 for (sal_uInt16 nNum2
=0; nNum2
<nCount2
; nNum2
++)
832 if (bAuto2
) rCon2
.nConId
=nNum2
;
833 if (bCon2
&& rCon2
.TakeGluePoint(aGP2
))
836 nEsc2
=aGP2
.GetEscDir();
837 if (nEsc2
==SdrEscapeDirection::SMART
) nEsc2
=ImpCalcEscAngle(rCon2
.pObj
,aPt2
-rCon2
.aObjOfs
);
839 for (tools::Long nA1
=0; nA1
<36000; nA1
+=9000)
841 SdrEscapeDirection nE1
= nA1
==0 ? SdrEscapeDirection::RIGHT
: nA1
==9000 ? SdrEscapeDirection::TOP
: nA1
==18000 ? SdrEscapeDirection::LEFT
: nA1
==27000 ? SdrEscapeDirection::BOTTOM
: SdrEscapeDirection::SMART
;
842 for (tools::Long nA2
=0; nA2
<36000; nA2
+=9000)
844 SdrEscapeDirection nE2
= nA2
==0 ? SdrEscapeDirection::RIGHT
: nA2
==9000 ? SdrEscapeDirection::TOP
: nA2
==18000 ? SdrEscapeDirection::LEFT
: nA2
==27000 ? SdrEscapeDirection::BOTTOM
: SdrEscapeDirection::SMART
;
845 if ((nEsc1
&nE1
) && (nEsc2
&nE2
))
848 SdrEdgeInfoRec aInfo
;
849 if (pInfo
!=nullptr) aInfo
=*pInfo
;
850 XPolygon
aXP(ImpCalcEdgeTrack(aPt1
,nA1
,aBoundRect1
,aBewareRect1
,aPt2
,nA2
,aBoundRect2
,aBewareRect2
,&nQual
,&aInfo
));
853 aBestXP
=std::move(aXP
);
864 if (bAuto1
) rCon1
.nConId
=nBestAuto1
;
865 if (bAuto2
) rCon2
.nConId
=nBestAuto2
;
866 if (pInfo
!=nullptr) *pInfo
=aBestInfo
;
870 XPolygon
SdrEdgeObj::ImpCalcEdgeTrack(const Point
& rPt1
, tools::Long nAngle1
, const tools::Rectangle
& rBoundRect1
, const tools::Rectangle
& rBewareRect1
,
871 const Point
& rPt2
, tools::Long nAngle2
, const tools::Rectangle
& rBoundRect2
, const tools::Rectangle
& rBewareRect2
,
872 sal_uIntPtr
* pnQuality
, SdrEdgeInfoRec
* pInfo
) const
874 SdrEdgeKind eKind
=GetObjectItem(SDRATTR_EDGEKIND
).GetValue();
875 bool bRts1
=nAngle1
==0;
876 bool bObn1
=nAngle1
==9000;
877 bool bLks1
=nAngle1
==18000;
878 bool bUnt1
=nAngle1
==27000;
879 bool bHor1
=bLks1
|| bRts1
;
880 bool bVer1
=bObn1
|| bUnt1
;
881 bool bRts2
=nAngle2
==0;
882 bool bObn2
=nAngle2
==9000;
883 bool bLks2
=nAngle2
==18000;
884 bool bUnt2
=nAngle2
==27000;
885 bool bHor2
=bLks2
|| bRts2
;
886 bool bVer2
=bObn2
|| bUnt2
;
887 bool bInfo
=pInfo
!=nullptr;
889 pInfo
->nAngle1
=nAngle1
;
890 pInfo
->nAngle2
=nAngle2
;
893 pInfo
->nMiddleLine
=0xFFFF;
897 tools::Rectangle
aBoundRect1 (rBoundRect1
);
898 tools::Rectangle
aBoundRect2 (rBoundRect2
);
899 tools::Rectangle
aBewareRect1(rBewareRect1
);
900 tools::Rectangle
aBewareRect2(rBewareRect2
);
901 Point
aMeeting((aPt1
.X()+aPt2
.X()+1)/2,(aPt1
.Y()+aPt2
.Y()+1)/2);
902 if (eKind
==SdrEdgeKind::OneLine
) {
906 if (pnQuality
!=nullptr) {
907 *pnQuality
=std::abs(rPt1
.X()-rPt2
.X())+std::abs(rPt1
.Y()-rPt2
.Y());
910 } else if (eKind
==SdrEdgeKind::ThreeLines
) {
916 if (bRts1
) aXP
[1].setX(aBewareRect1
.Right() ); //+=500;
917 if (bObn1
) aXP
[1].setY(aBewareRect1
.Top() ); //-=500;
918 if (bLks1
) aXP
[1].setX(aBewareRect1
.Left() ); //-=500;
919 if (bUnt1
) aXP
[1].setY(aBewareRect1
.Bottom() ); //+=500;
920 if (bRts2
) aXP
[2].setX(aBewareRect2
.Right() ); //+=500;
921 if (bObn2
) aXP
[2].setY(aBewareRect2
.Top() ); //-=500;
922 if (bLks2
) aXP
[2].setX(aBewareRect2
.Left() ); //-=500;
923 if (bUnt2
) aXP
[2].setY(aBewareRect2
.Bottom() ); //+=500;
924 if (pnQuality
!=nullptr) {
925 tools::Long nQ
=std::abs(aXP
[1].X()-aXP
[0].X())+std::abs(aXP
[1].Y()-aXP
[0].Y());
926 nQ
+=std::abs(aXP
[2].X()-aXP
[1].X())+std::abs(aXP
[2].Y()-aXP
[1].Y());
927 nQ
+=std::abs(aXP
[3].X()-aXP
[2].X())+std::abs(aXP
[3].Y()-aXP
[2].Y());
934 aXP
[1].AdjustX(pInfo
->aObj1Line2
.X() );
936 aXP
[1].AdjustY(pInfo
->aObj1Line2
.Y() );
939 aXP
[2].AdjustX(pInfo
->aObj2Line2
.X() );
941 aXP
[2].AdjustY(pInfo
->aObj2Line2
.Y() );
946 sal_uInt16 nIntersections
=0;
948 Point
aC1(aBewareRect1
.Center());
949 Point
aC2(aBewareRect2
.Center());
950 if (aBewareRect1
.Left()<=aBewareRect2
.Right() && aBewareRect1
.Right()>=aBewareRect2
.Left()) {
951 // overlapping on the x axis
952 tools::Long n1
=std::max(aBewareRect1
.Left(),aBewareRect2
.Left());
953 tools::Long n2
=std::min(aBewareRect1
.Right(),aBewareRect2
.Right());
954 aMeeting
.setX((n1
+n2
+1)/2 );
956 // otherwise the center point of the empty space
957 if (aC1
.X()<aC2
.X()) {
958 aMeeting
.setX((aBewareRect1
.Right()+aBewareRect2
.Left()+1)/2 );
960 aMeeting
.setX((aBewareRect1
.Left()+aBewareRect2
.Right()+1)/2 );
963 if (aBewareRect1
.Top()<=aBewareRect2
.Bottom() && aBewareRect1
.Bottom()>=aBewareRect2
.Top()) {
964 // overlapping on the x axis
965 tools::Long n1
=std::max(aBewareRect1
.Top(),aBewareRect2
.Top());
966 tools::Long n2
=std::min(aBewareRect1
.Bottom(),aBewareRect2
.Bottom());
967 aMeeting
.setY((n1
+n2
+1)/2 );
969 // otherwise the center point of the empty space
970 if (aC1
.Y()<aC2
.Y()) {
971 aMeeting
.setY((aBewareRect1
.Bottom()+aBewareRect2
.Top()+1)/2 );
973 aMeeting
.setY((aBewareRect1
.Top()+aBewareRect2
.Bottom()+1)/2 );
976 // Here, there are three cases:
977 // 1. both go into the same direction
978 // 2. both go into opposite directions
979 // 3. one is vertical, the other is horizontal
980 tools::Long nXMin
=std::min(aBewareRect1
.Left(),aBewareRect2
.Left());
981 tools::Long nXMax
=std::max(aBewareRect1
.Right(),aBewareRect2
.Right());
982 tools::Long nYMin
=std::min(aBewareRect1
.Top(),aBewareRect2
.Top());
983 tools::Long nYMax
=std::max(aBewareRect1
.Bottom(),aBewareRect2
.Bottom());
984 bool bBewareOverlap
=aBewareRect1
.Right()>aBewareRect2
.Left() && aBewareRect1
.Left()<aBewareRect2
.Right() &&
985 aBewareRect1
.Bottom()>aBewareRect2
.Top() && aBewareRect1
.Top()<aBewareRect2
.Bottom();
986 unsigned nMainCase
=3;
987 if (nAngle1
==nAngle2
) nMainCase
=1;
988 else if ((bHor1
&& bHor2
) || (bVer1
&& bVer2
)) nMainCase
=2;
989 if (nMainCase
==1) { // case 1 (both go in one direction) is possible
990 if (bVer1
) aMeeting
.setX((aPt1
.X()+aPt2
.X()+1)/2 ); // Here, this is better than
991 if (bHor1
) aMeeting
.setY((aPt1
.Y()+aPt2
.Y()+1)/2 ); // using center point of empty space
992 // bX1Ok means that the vertical exiting Obj1 doesn't conflict with Obj2, ...
993 bool bX1Ok
=aPt1
.X()<=aBewareRect2
.Left() || aPt1
.X()>=aBewareRect2
.Right();
994 bool bX2Ok
=aPt2
.X()<=aBewareRect1
.Left() || aPt2
.X()>=aBewareRect1
.Right();
995 bool bY1Ok
=aPt1
.Y()<=aBewareRect2
.Top() || aPt1
.Y()>=aBewareRect2
.Bottom();
996 bool bY2Ok
=aPt2
.Y()<=aBewareRect1
.Top() || aPt2
.Y()>=aBewareRect1
.Bottom();
997 if (bLks1
&& (bY1Ok
|| aBewareRect1
.Left()<aBewareRect2
.Right()) && (bY2Ok
|| aBewareRect2
.Left()<aBewareRect1
.Right())) {
998 aMeeting
.setX(nXMin
);
1000 if (bRts1
&& (bY1Ok
|| aBewareRect1
.Right()>aBewareRect2
.Left()) && (bY2Ok
|| aBewareRect2
.Right()>aBewareRect1
.Left())) {
1001 aMeeting
.setX(nXMax
);
1003 if (bObn1
&& (bX1Ok
|| aBewareRect1
.Top()<aBewareRect2
.Bottom()) && (bX2Ok
|| aBewareRect2
.Top()<aBewareRect1
.Bottom())) {
1004 aMeeting
.setY(nYMin
);
1006 if (bUnt1
&& (bX1Ok
|| aBewareRect1
.Bottom()>aBewareRect2
.Top()) && (bX2Ok
|| aBewareRect2
.Bottom()>aBewareRect1
.Top())) {
1007 aMeeting
.setY(nYMax
);
1009 } else if (nMainCase
==2) {
1011 if (bHor1
) { // both horizontal
1013 (legend: line exits to the left (-|), right (|-))
1015 2.1: Facing; overlap only on y axis
1020 2.2, 2.3: Facing, offset vertically; no overlap on either
1026 2.4, 2.5: One below the other; overlap only on y axis
1031 2.6, 2.7: Not facing, offset vertically; no overlap on either
1037 2.8: Not facing; overlap only on y axis
1042 2.9: The objects's BewareRects overlap on x and y axis
1044 These cases, with some modifications are also valid for
1045 horizontal line exits.
1046 Cases 2.1 through 2.7 are covered well enough with the
1047 default meetings. Only for cases 2.8 and 2.9 do we determine
1048 special meeting points here.
1051 // normalization; be aR1 the one exiting to the right,
1052 // be aR2 the one exiting to the left
1053 tools::Rectangle
aBewR1(bRts1
? aBewareRect1
: aBewareRect2
);
1054 tools::Rectangle
aBewR2(bRts1
? aBewareRect2
: aBewareRect1
);
1055 tools::Rectangle
aBndR1(bRts1
? aBoundRect1
: aBoundRect2
);
1056 tools::Rectangle
aBndR2(bRts1
? aBoundRect2
: aBoundRect1
);
1057 if (aBewR1
.Bottom()>aBewR2
.Top() && aBewR1
.Top()<aBewR2
.Bottom()) {
1058 // overlap on y axis; cases 2.1, 2.8, 2.9
1059 if (aBewR1
.Right()>aBewR2
.Left()) {
1061 Case 2.8: always going around on the outside
1064 Case 2.9 could also be a direct connection (in the
1065 case that the BewareRects overlap only slightly and
1066 the BoundRects don't overlap at all and if the
1067 line exits would otherwise violate the respective
1068 other object's BewareRect).
1070 bool bCase29Direct
= false;
1071 bool bCase29
=aBewR1
.Right()>aBewR2
.Left();
1072 if (aBndR1
.Right()<=aBndR2
.Left()) { // case 2.9 without BoundRect overlap
1073 if ((aPt1
.Y()>aBewareRect2
.Top() && aPt1
.Y()<aBewareRect2
.Bottom()) ||
1074 (aPt2
.Y()>aBewareRect1
.Top() && aPt2
.Y()<aBewareRect1
.Bottom())) {
1075 bCase29Direct
= true;
1078 if (!bCase29Direct
) {
1079 bool bObenLang
=std::abs(nYMin
-aMeeting
.Y())<=std::abs(nYMax
-aMeeting
.Y());
1081 aMeeting
.setY(nYMin
);
1083 aMeeting
.setY(nYMax
);
1086 // now make sure that the surrounded object
1088 if ((aBewR1
.Center().Y()<aBewR2
.Center().Y()) != bObenLang
) {
1089 aMeeting
.setX(aBewR2
.Right() );
1091 aMeeting
.setX(aBewR1
.Left() );
1095 // We need a direct connection (3-line Z connection),
1096 // because we have to violate the BewareRects.
1097 // Use rule of three to scale down the BewareRects.
1098 tools::Long nWant1
=aBewR1
.Right()-aBndR1
.Right(); // distance at Obj1
1099 tools::Long nWant2
=aBndR2
.Left()-aBewR2
.Left(); // distance at Obj2
1100 tools::Long nSpace
=aBndR2
.Left()-aBndR1
.Right(); // available space
1101 tools::Long nGet1
=BigMulDiv(nWant1
,nSpace
,nWant1
+nWant2
);
1102 tools::Long nGet2
=nSpace
-nGet1
;
1103 if (bRts1
) { // revert normalization
1104 aBewareRect1
.AdjustRight(nGet1
-nWant1
);
1105 aBewareRect2
.AdjustLeft( -(nGet2
-nWant2
) );
1107 aBewareRect2
.AdjustRight(nGet1
-nWant1
);
1108 aBewareRect1
.AdjustLeft( -(nGet2
-nWant2
) );
1110 nIntersections
++; // lower quality
1114 } else if (bVer1
) { // both horizontal
1115 tools::Rectangle
aBewR1(bUnt1
? aBewareRect1
: aBewareRect2
);
1116 tools::Rectangle
aBewR2(bUnt1
? aBewareRect2
: aBewareRect1
);
1117 tools::Rectangle
aBndR1(bUnt1
? aBoundRect1
: aBoundRect2
);
1118 tools::Rectangle
aBndR2(bUnt1
? aBoundRect2
: aBoundRect1
);
1119 if (aBewR1
.Right()>aBewR2
.Left() && aBewR1
.Left()<aBewR2
.Right()) {
1120 // overlap on y axis; cases 2.1, 2.8, 2.9
1121 if (aBewR1
.Bottom()>aBewR2
.Top()) {
1123 Case 2.8 always going around on the outside (bDirect=false).
1125 Case 2.9 could also be a direct connection (in the
1126 case that the BewareRects overlap only slightly and
1127 the BoundRects don't overlap at all and if the
1128 line exits would otherwise violate the respective
1129 other object's BewareRect).
1131 bool bCase29Direct
= false;
1132 bool bCase29
=aBewR1
.Bottom()>aBewR2
.Top();
1133 if (aBndR1
.Bottom()<=aBndR2
.Top()) { // case 2.9 without BoundRect overlap
1134 if ((aPt1
.X()>aBewareRect2
.Left() && aPt1
.X()<aBewareRect2
.Right()) ||
1135 (aPt2
.X()>aBewareRect1
.Left() && aPt2
.X()<aBewareRect1
.Right())) {
1136 bCase29Direct
= true;
1139 if (!bCase29Direct
) {
1140 bool bLinksLang
=std::abs(nXMin
-aMeeting
.X())<=std::abs(nXMax
-aMeeting
.X());
1142 aMeeting
.setX(nXMin
);
1144 aMeeting
.setX(nXMax
);
1147 // now make sure that the surrounded object
1149 if ((aBewR1
.Center().X()<aBewR2
.Center().X()) != bLinksLang
) {
1150 aMeeting
.setY(aBewR2
.Bottom() );
1152 aMeeting
.setY(aBewR1
.Top() );
1156 // We need a direct connection (3-line Z connection),
1157 // because we have to violate the BewareRects.
1158 // Use rule of three to scale down the BewareRects.
1159 tools::Long nWant1
=aBewR1
.Bottom()-aBndR1
.Bottom(); // difference at Obj1
1160 tools::Long nWant2
=aBndR2
.Top()-aBewR2
.Top(); // difference at Obj2
1161 tools::Long nSpace
=aBndR2
.Top()-aBndR1
.Bottom(); // available space
1162 tools::Long nGet1
=BigMulDiv(nWant1
,nSpace
,nWant1
+nWant2
);
1163 tools::Long nGet2
=nSpace
-nGet1
;
1164 if (bUnt1
) { // revert normalization
1165 aBewareRect1
.AdjustBottom(nGet1
-nWant1
);
1166 aBewareRect2
.AdjustTop( -(nGet2
-nWant2
) );
1168 aBewareRect2
.AdjustBottom(nGet1
-nWant1
);
1169 aBewareRect1
.AdjustTop( -(nGet2
-nWant2
) );
1171 nIntersections
++; // lower quality
1176 } else if (nMainCase
==3) { // case 3: one horizontal, the other vertical
1178 The line exits to the:
1187 * . * . * -- no overlap, at most might touch
1188 . . . . . -- overlap
1189 * . |- . * -- same height
1190 . . . . . -- overlap
1191 * . * . * -- no overlap, at most might touch
1193 Overall, there are 96 possible constellations, some of these can't even
1194 be unambiguously assigned to a certain case/method of handling.
1197 3.1: All those constellations that are covered reasonably well
1198 by the default MeetingPoint (20+12).
1200 T T T . _|_ _|_ . T T T these 12 * . * T * * . * . * * T * . * * . * . *
1201 . . . . _|_ _|_ . . . . constellations . . . . . . . . . T . . . . . T . . . .
1202 * . |- . * * . -| . * are covered * . |- . _|_ * . |- . T _|_ . -| . * T . -| . *
1203 . . . . T T . . . . only in . . . . _|_ . . . . . _|_ . . . . . . . . .
1204 _|__|__|_ . T T . _|__|__|_ part: * . * _|_ * * . * . * * _|_ * . * * . * . *
1206 The last 16 of these cases can be excluded, if the objects face each other openly.
1209 3.2: The objects face each other openly, thus a connection using only two lines is possible (4+20);
1210 This case is priority #1.
1211 * . * . T T . * . * these 20 * . * T * * T * . * * . * . * * . * . *
1212 . . . . . . . . . . constellations . . . T T T T . . . . . . . . . . . . .
1213 * . |- . * * . -| . * are covered * . |-_|__|_ _|__|_-| . * * . |- T T T T -| . *
1214 . . . . . . . . . . only in . . . _|__|_ _|__|_ . . . . . . . . . . . . .
1215 * . * . _|_ _|_ . * . * part: * . * _|_ * * _|_ * . * * . * . * * . * . *
1217 3.3: The line exits point away from the other object or miss its back (52+4).
1218 _|__|__|__|_ * * _|__|__|__|_ * . . . * * . * . * these 4 * . * . * * . * . *
1219 _|__|__|__|_ . . _|__|__|__|_ T T T . . . . T T T constellations . . . T . . T . . .
1220 _|__|_ |- . * * . -| _|__|_ T T |- . * * . -| T T are covered * . |- . * * . -| . *
1221 _|__|__|_ . . . . _|__|__|_ T T T T . . T T T T only in . . . _|_ . . _|_ . . .
1222 * . * . * * . * . * T T T T * * T T T T part: * . * . * * . * . *
1226 tools::Rectangle
aTmpR1(aBewareRect1
);
1227 tools::Rectangle
aTmpR2(aBewareRect2
);
1228 if (bBewareOverlap
) {
1229 // overlapping BewareRects: use BoundRects for checking for case 3.2
1233 if ((((bRts1
&& aTmpR1
.Right ()<=aPt2
.X()) || (bLks1
&& aTmpR1
.Left()>=aPt2
.X())) &&
1234 ((bUnt2
&& aTmpR2
.Bottom()<=aPt1
.Y()) || (bObn2
&& aTmpR2
.Top ()>=aPt1
.Y()))) ||
1235 (((bRts2
&& aTmpR2
.Right ()<=aPt1
.X()) || (bLks2
&& aTmpR2
.Left()>=aPt1
.X())) &&
1236 ((bUnt1
&& aTmpR1
.Bottom()<=aPt2
.Y()) || (bObn1
&& aTmpR1
.Top ()>=aPt2
.Y())))) {
1237 // case 3.2 applies: connector with only 2 lines
1239 aMeeting
.setX(aPt2
.X() );
1240 aMeeting
.setY(aPt1
.Y() );
1242 aMeeting
.setX(aPt1
.X() );
1243 aMeeting
.setY(aPt2
.Y() );
1245 // in the case of overlapping BewareRects:
1246 aBewareRect1
=aTmpR1
;
1247 aBewareRect2
=aTmpR2
;
1248 } else if ((((bRts1
&& aBewareRect1
.Right ()>aBewareRect2
.Left ()) ||
1249 (bLks1
&& aBewareRect1
.Left ()<aBewareRect2
.Right ())) &&
1250 ((bUnt2
&& aBewareRect2
.Bottom()>aBewareRect1
.Top ()) ||
1251 (bObn2
&& aBewareRect2
.Top ()<aBewareRect1
.Bottom()))) ||
1252 (((bRts2
&& aBewareRect2
.Right ()>aBewareRect1
.Left ()) ||
1253 (bLks2
&& aBewareRect2
.Left ()<aBewareRect1
.Right ())) &&
1254 ((bUnt1
&& aBewareRect1
.Bottom()>aBewareRect2
.Top ()) ||
1255 (bObn1
&& aBewareRect1
.Top ()<aBewareRect2
.Bottom())))) {
1257 if (bRts1
|| bRts2
) { aMeeting
.setX(nXMax
); }
1258 if (bLks1
|| bLks2
) { aMeeting
.setX(nXMin
); }
1259 if (bUnt1
|| bUnt2
) { aMeeting
.setY(nYMax
); }
1260 if (bObn1
|| bObn2
) { aMeeting
.setY(nYMin
); }
1265 XPolygon
aXP1(ImpCalcObjToCenter(aPt1
,nAngle1
,aBewareRect1
,aMeeting
));
1266 XPolygon
aXP2(ImpCalcObjToCenter(aPt2
,nAngle2
,aBewareRect2
,aMeeting
));
1267 sal_uInt16 nXP1Cnt
=aXP1
.GetPointCount();
1268 sal_uInt16 nXP2Cnt
=aXP2
.GetPointCount();
1270 pInfo
->nObj1Lines
=nXP1Cnt
; if (nXP1Cnt
>1) pInfo
->nObj1Lines
--;
1271 pInfo
->nObj2Lines
=nXP2Cnt
; if (nXP2Cnt
>1) pInfo
->nObj2Lines
--;
1273 Point
aEP1(aXP1
[nXP1Cnt
-1]);
1274 Point
aEP2(aXP2
[nXP2Cnt
-1]);
1275 bool bInsMeetingPoint
=aEP1
.X()!=aEP2
.X() && aEP1
.Y()!=aEP2
.Y();
1276 bool bHorzE1
=aEP1
.Y()==aXP1
[nXP1Cnt
-2].Y(); // is last line of XP1 horizontal?
1277 bool bHorzE2
=aEP2
.Y()==aXP2
[nXP2Cnt
-2].Y(); // is last line of XP2 horizontal?
1278 if (aEP1
==aEP2
&& ((bHorzE1
&& bHorzE2
&& aEP1
.Y()==aEP2
.Y()) || (!bHorzE1
&& !bHorzE2
&& aEP1
.X()==aEP2
.X()))) {
1279 // special casing 'I' connectors
1280 nXP1Cnt
--; aXP1
.Remove(nXP1Cnt
,1);
1281 nXP2Cnt
--; aXP2
.Remove(nXP2Cnt
,1);
1283 if (bInsMeetingPoint
) {
1284 aXP1
.Insert(XPOLY_APPEND
,aMeeting
,PolyFlags::Normal
);
1286 // Inserting a MeetingPoint adds 2 new lines,
1287 // either might become the center line.
1288 if (pInfo
->nObj1Lines
==pInfo
->nObj2Lines
) {
1289 pInfo
->nObj1Lines
++;
1290 pInfo
->nObj2Lines
++;
1292 if (pInfo
->nObj1Lines
>pInfo
->nObj2Lines
) {
1293 pInfo
->nObj2Lines
++;
1294 pInfo
->nMiddleLine
=nXP1Cnt
-1;
1296 pInfo
->nObj1Lines
++;
1297 pInfo
->nMiddleLine
=nXP1Cnt
;
1301 } else if (bInfo
&& aEP1
!=aEP2
&& nXP1Cnt
+nXP2Cnt
>=4) {
1302 // By connecting both ends, another line is added, this becomes the center line.
1303 pInfo
->nMiddleLine
=nXP1Cnt
-1;
1305 sal_uInt16 nNum
=aXP2
.GetPointCount();
1306 if (aXP1
[nXP1Cnt
-1]==aXP2
[nXP2Cnt
-1] && nXP1Cnt
>1 && nXP2Cnt
>1) nNum
--;
1309 aXP1
.Insert(XPOLY_APPEND
,aXP2
[nNum
],PolyFlags::Normal
);
1311 sal_uInt16 nPointCount
=aXP1
.GetPointCount();
1313 if (bInfo
|| pnQuality
!=nullptr) {
1314 if (nPointCount
==2) cForm
='I';
1315 else if (nPointCount
==3) cForm
='L';
1316 else if (nPointCount
==4) { // Z or U
1317 if (nAngle1
==nAngle2
) cForm
='U';
1319 } else if (nPointCount
==6) { // S or C or ...
1320 if (nAngle1
!=nAngle2
) {
1321 // For type S, line 2 has the same direction as line 4.
1322 // For type C, the opposite is true.
1327 if (aP1
.Y()==aP2
.Y()) { // else both lines are horizontal
1328 if ((aP1
.X()<aP2
.X())==(aP3
.X()<aP4
.X())) cForm
='S';
1330 } else { // else both lines are vertical
1331 if ((aP1
.Y()<aP2
.Y())==(aP3
.Y()<aP4
.Y())) cForm
='S';
1334 } else cForm
='4'; // else is case 3 with 5 lines
1338 if (cForm
=='I' || cForm
=='L' || cForm
=='Z' || cForm
=='U') {
1339 pInfo
->nObj1Lines
=1;
1340 pInfo
->nObj2Lines
=1;
1341 if (cForm
=='Z' || cForm
=='U') {
1342 pInfo
->nMiddleLine
=1;
1344 pInfo
->nMiddleLine
=0xFFFF;
1346 } else if (cForm
=='S' || cForm
=='C') {
1347 pInfo
->nObj1Lines
=2;
1348 pInfo
->nObj2Lines
=2;
1349 pInfo
->nMiddleLine
=2;
1357 if (pnQuality
!=nullptr) {
1358 sal_uIntPtr nQual
=0;
1359 sal_uIntPtr nQual0
=nQual
; // prevent overruns
1360 bool bOverflow
= false;
1361 Point
aPt0(aXP1
[0]);
1362 for (sal_uInt16 nPntNum
=1; nPntNum
<nPointCount
; nPntNum
++) {
1363 Point
aPt1b(aXP1
[nPntNum
]);
1364 nQual
+=std::abs(aPt1b
.X()-aPt0
.X())+std::abs(aPt1b
.Y()-aPt0
.Y());
1365 if (nQual
<nQual0
) bOverflow
= true;
1370 sal_uInt16 nTmp
=nPointCount
;
1372 nTmp
=2; // Z shape with good quality (nTmp=2 instead of 4)
1373 sal_uIntPtr n1
=std::abs(aXP1
[1].X()-aXP1
[0].X())+std::abs(aXP1
[1].Y()-aXP1
[0].Y());
1374 sal_uIntPtr n2
=std::abs(aXP1
[2].X()-aXP1
[1].X())+std::abs(aXP1
[2].Y()-aXP1
[1].Y());
1375 sal_uIntPtr n3
=std::abs(aXP1
[3].X()-aXP1
[2].X())+std::abs(aXP1
[3].Y()-aXP1
[2].Y());
1376 // try to make lines lengths similar
1377 sal_uIntPtr nBesser
=0;
1380 if (n1
>=n2
) nBesser
=6;
1381 else if (n1
>=3*n3
) nBesser
=4;
1382 else if (n1
>=2*n3
) nBesser
=2;
1383 if (aXP1
[0].Y()!=aXP1
[1].Y()) nBesser
++; // vertical starting line gets a plus (for H/V-Prio)
1384 if (nQual
>nBesser
) nQual
-=nBesser
; else nQual
=0;
1388 nQual
+=static_cast<sal_uIntPtr
>(nTmp
)*0x01000000;
1389 if (nQual
<nQual0
|| nTmp
>15) bOverflow
= true;
1391 if (nPointCount
>=2) { // check exit angle again
1392 Point
aP1(aXP1
[1]); aP1
-=aXP1
[0];
1393 Point
aP2(aXP1
[nPointCount
-2]); aP2
-=aXP1
[nPointCount
-1];
1394 tools::Long nAng1
=0; if (aP1
.X()<0) nAng1
=18000; if (aP1
.Y()>0) nAng1
=27000;
1395 if (aP1
.Y()<0) nAng1
=9000;
1396 if (aP1
.X()!=0 && aP1
.Y()!=0) nAng1
=1; // slant?!
1397 tools::Long nAng2
=0; if (aP2
.X()<0) nAng2
=18000; if (aP2
.Y()>0) nAng2
=27000;
1398 if (aP2
.Y()<0) nAng2
=9000;
1399 if (aP2
.X()!=0 && aP2
.Y()!=0) nAng2
=1; // slant?!
1400 if (nAng1
!=nAngle1
) nIntersections
++;
1401 if (nAng2
!=nAngle2
) nIntersections
++;
1404 // For the quality check, use the original Rects and at the same time
1405 // check whether one them was scaled down for the calculation of the
1406 // Edges (e. g. case 2.9)
1407 aBewareRect1
=rBewareRect1
;
1408 aBewareRect2
=rBewareRect2
;
1410 for (sal_uInt16 i
=0; i
<nPointCount
; i
++) {
1411 Point
aPt1b(aXP1
[i
]);
1412 bool b1
=aPt1b
.X()>aBewareRect1
.Left() && aPt1b
.X()<aBewareRect1
.Right() &&
1413 aPt1b
.Y()>aBewareRect1
.Top() && aPt1b
.Y()<aBewareRect1
.Bottom();
1414 bool b2
=aPt1b
.X()>aBewareRect2
.Left() && aPt1b
.X()<aBewareRect2
.Right() &&
1415 aPt1b
.Y()>aBewareRect2
.Top() && aPt1b
.Y()<aBewareRect2
.Bottom();
1416 sal_uInt16 nInt0
=nIntersections
;
1417 if (i
==0 || i
==nPointCount
-1) {
1418 if (b1
&& b2
) nIntersections
++;
1420 if (b1
) nIntersections
++;
1421 if (b2
) nIntersections
++;
1423 // check for overlaps
1424 if (i
>0 && nInt0
==nIntersections
) {
1425 if (aPt0
.Y()==aPt1b
.Y()) { // horizontal line
1426 if (aPt0
.Y()>aBewareRect1
.Top() && aPt0
.Y()<aBewareRect1
.Bottom() &&
1427 ((aPt0
.X()<=aBewareRect1
.Left() && aPt1b
.X()>=aBewareRect1
.Right()) ||
1428 (aPt1b
.X()<=aBewareRect1
.Left() && aPt0
.X()>=aBewareRect1
.Right()))) nIntersections
++;
1429 if (aPt0
.Y()>aBewareRect2
.Top() && aPt0
.Y()<aBewareRect2
.Bottom() &&
1430 ((aPt0
.X()<=aBewareRect2
.Left() && aPt1b
.X()>=aBewareRect2
.Right()) ||
1431 (aPt1b
.X()<=aBewareRect2
.Left() && aPt0
.X()>=aBewareRect2
.Right()))) nIntersections
++;
1432 } else { // vertical line
1433 if (aPt0
.X()>aBewareRect1
.Left() && aPt0
.X()<aBewareRect1
.Right() &&
1434 ((aPt0
.Y()<=aBewareRect1
.Top() && aPt1b
.Y()>=aBewareRect1
.Bottom()) ||
1435 (aPt1b
.Y()<=aBewareRect1
.Top() && aPt0
.Y()>=aBewareRect1
.Bottom()))) nIntersections
++;
1436 if (aPt0
.X()>aBewareRect2
.Left() && aPt0
.X()<aBewareRect2
.Right() &&
1437 ((aPt0
.Y()<=aBewareRect2
.Top() && aPt1b
.Y()>=aBewareRect2
.Bottom()) ||
1438 (aPt1b
.Y()<=aBewareRect2
.Top() && aPt0
.Y()>=aBewareRect2
.Bottom()))) nIntersections
++;
1443 if (nPointCount
<=1) nIntersections
++;
1445 nQual
+=static_cast<sal_uIntPtr
>(nIntersections
)*0x10000000;
1446 if (nQual
<nQual0
|| nIntersections
>15) bOverflow
= true;
1448 if (bOverflow
|| nQual
==0xFFFFFFFF) nQual
=0xFFFFFFFE;
1451 if (bInfo
) { // now apply line offsets to aXP1
1452 if (pInfo
->nMiddleLine
!=0xFFFF) {
1453 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(SdrEdgeLineCode::MiddleLine
,aXP1
);
1454 if (pInfo
->ImpIsHorzLine(SdrEdgeLineCode::MiddleLine
,aXP1
)) {
1455 aXP1
[nIdx
].AdjustY(pInfo
->aMiddleLine
.Y() );
1456 aXP1
[nIdx
+1].AdjustY(pInfo
->aMiddleLine
.Y() );
1458 aXP1
[nIdx
].AdjustX(pInfo
->aMiddleLine
.X() );
1459 aXP1
[nIdx
+1].AdjustX(pInfo
->aMiddleLine
.X() );
1462 if (pInfo
->nObj1Lines
>=2) {
1463 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line2
,aXP1
);
1464 if (pInfo
->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line2
,aXP1
)) {
1465 aXP1
[nIdx
].AdjustY(pInfo
->aObj1Line2
.Y() );
1466 aXP1
[nIdx
+1].AdjustY(pInfo
->aObj1Line2
.Y() );
1468 aXP1
[nIdx
].AdjustX(pInfo
->aObj1Line2
.X() );
1469 aXP1
[nIdx
+1].AdjustX(pInfo
->aObj1Line2
.X() );
1472 if (pInfo
->nObj1Lines
>=3) {
1473 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line3
,aXP1
);
1474 if (pInfo
->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line3
,aXP1
)) {
1475 aXP1
[nIdx
].AdjustY(pInfo
->aObj1Line3
.Y() );
1476 aXP1
[nIdx
+1].AdjustY(pInfo
->aObj1Line3
.Y() );
1478 aXP1
[nIdx
].AdjustX(pInfo
->aObj1Line3
.X() );
1479 aXP1
[nIdx
+1].AdjustX(pInfo
->aObj1Line3
.X() );
1482 if (pInfo
->nObj2Lines
>=2) {
1483 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line2
,aXP1
);
1484 if (pInfo
->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line2
,aXP1
)) {
1485 aXP1
[nIdx
].AdjustY(pInfo
->aObj2Line2
.Y() );
1486 aXP1
[nIdx
+1].AdjustY(pInfo
->aObj2Line2
.Y() );
1488 aXP1
[nIdx
].AdjustX(pInfo
->aObj2Line2
.X() );
1489 aXP1
[nIdx
+1].AdjustX(pInfo
->aObj2Line2
.X() );
1492 if (pInfo
->nObj2Lines
>=3) {
1493 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line3
,aXP1
);
1494 if (pInfo
->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line3
,aXP1
)) {
1495 aXP1
[nIdx
].AdjustY(pInfo
->aObj2Line3
.Y() );
1496 aXP1
[nIdx
+1].AdjustY(pInfo
->aObj2Line3
.Y() );
1498 aXP1
[nIdx
].AdjustX(pInfo
->aObj2Line3
.X() );
1499 aXP1
[nIdx
+1].AdjustX(pInfo
->aObj2Line3
.X() );
1503 // make the connector a bezier curve, if appropriate
1504 if (eKind
==SdrEdgeKind::Bezier
&& nPointCount
>2) {
1505 Point
* pPt1
=&aXP1
[0];
1506 Point
* pPt2
=&aXP1
[1];
1507 Point
* pPt3
=&aXP1
[nPointCount
-2];
1508 Point
* pPt4
=&aXP1
[nPointCount
-1];
1509 tools::Long dx1
=pPt2
->X()-pPt1
->X();
1510 tools::Long dy1
=pPt2
->Y()-pPt1
->Y();
1511 tools::Long dx2
=pPt3
->X()-pPt4
->X();
1512 tools::Long dy2
=pPt3
->Y()-pPt4
->Y();
1513 if (cForm
=='L') { // nPointCount==3
1514 aXP1
.SetFlags(1,PolyFlags::Control
);
1516 aXP1
.Insert(2,aPt3
,PolyFlags::Control
);
1517 nPointCount
=aXP1
.GetPointCount();
1519 pPt3
=&aXP1
[nPointCount
-2];
1520 pPt2
->AdjustX( -(dx1
/3) );
1521 pPt2
->AdjustY( -(dy1
/3) );
1522 pPt3
->AdjustX( -(dx2
/3) );
1523 pPt3
->AdjustY( -(dy2
/3) );
1524 } else if (nPointCount
>=4 && nPointCount
<=6) { // Z or U or ...
1525 // To all others, the end points of the original lines become control
1526 // points for now. Thus, we need to do some more work for nPointCount>4!
1527 aXP1
.SetFlags(1,PolyFlags::Control
);
1528 aXP1
.SetFlags(nPointCount
-2,PolyFlags::Control
);
1530 pPt2
->AdjustX(dx1
/2 );
1531 pPt2
->AdjustY(dy1
/2 );
1532 pPt3
->AdjustX(dx2
/2 );
1533 pPt3
->AdjustY(dy2
/2 );
1534 if (nPointCount
==5) {
1535 // add a control point before and after center
1536 Point
aCenter(aXP1
[2]);
1537 tools::Long dx1b
=aCenter
.X()-aXP1
[1].X();
1538 tools::Long dy1b
=aCenter
.Y()-aXP1
[1].Y();
1539 tools::Long dx2b
=aCenter
.X()-aXP1
[3].X();
1540 tools::Long dy2b
=aCenter
.Y()-aXP1
[3].Y();
1541 aXP1
.Insert(2,aCenter
,PolyFlags::Control
);
1542 aXP1
.SetFlags(3,PolyFlags::Symmetric
);
1543 aXP1
.Insert(4,aCenter
,PolyFlags::Control
);
1544 aXP1
[2].AdjustX( -(dx1b
/2) );
1545 aXP1
[2].AdjustY( -(dy1b
/2) );
1546 aXP1
[3].AdjustX( -((dx1b
+dx2b
)/4) );
1547 aXP1
[3].AdjustY( -((dy1b
+dy2b
)/4) );
1548 aXP1
[4].AdjustX( -(dx2b
/2) );
1549 aXP1
[4].AdjustY( -(dy2b
/2) );
1551 if (nPointCount
==6) {
1552 Point
aPt1b(aXP1
[2]);
1553 Point
aPt2b(aXP1
[3]);
1554 aXP1
.Insert(2,aPt1b
,PolyFlags::Control
);
1555 aXP1
.Insert(5,aPt2b
,PolyFlags::Control
);
1556 tools::Long dx
=aPt1b
.X()-aPt2b
.X();
1557 tools::Long dy
=aPt1b
.Y()-aPt2b
.Y();
1558 aXP1
[3].AdjustX( -(dx
/2) );
1559 aXP1
[3].AdjustY( -(dy
/2) );
1560 aXP1
.SetFlags(3,PolyFlags::Symmetric
);
1561 aXP1
.Remove(4,1); // because it's identical with aXP1[3]
1569 There could be a maximum of 64 different developments with 5 lines, a
1570 maximum of 32 developments with 4 lines, a maximum of 16 developments with
1571 3 lines, a maximum of 8 developments with 2 lines.
1572 This gives us a total of 124 possibilities.
1573 Normalized for the 1st exit angle to the right, there remain 31 possibilities.
1574 Now, normalizing away the vertical mirroring, we get to a total of 16
1575 characteristic developments with 1 through 5 lines:
1577 1 line (type "I") --
1579 2 lines (type "L") __|
1581 3 lines (type "U") __ (type "Z") _
1584 4 lines #1 _| #2 | | #3 |_ #4 | |
1586 Of these, #1 is implausible, #2 is a rotated version of #3. This leaves
1587 #2 (from now on referred to as 4.1) and #4 (from now on referred to as 4.2).
1589 5 lines #1 _| #2 _| #3 ___ #4 _
1592 #5 |_ #6 |_ #7 _| | #8 ____
1594 Of these, 5.1, 5.2, 5.4 and 5.5 are implausible, 5.7 is a reversed version
1595 of 5.3. This leaves 5.3 (type "4"), 5.6 (type "S") and 5.8 (type "C").
1597 We now have discerned the 9 basic types to cover all 400 possible constellations
1598 of object positions and exit angles. 4 of the 9 types have got a center
1599 line (CL). The number of object margins per object varies between 0 and 3:
1606 4.2: y 0 1 = U+1, respectively 1+U
1607 4.4: n 0-2 0-2 = Z+1
1610 "C": n 0-3 0-3 = 1+U+1
1613 void SdrEdgeObj::Notify(SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
1615 const SfxHintId nId
= rHint
.GetId();
1616 bool bDataChg
=nId
==SfxHintId::DataChanged
;
1617 bool bDying
=nId
==SfxHintId::Dying
;
1618 bool bObj1
=aCon1
.pObj
!=nullptr && aCon1
.pObj
->GetBroadcaster()==&rBC
;
1619 bool bObj2
=aCon2
.pObj
!=nullptr && aCon2
.pObj
->GetBroadcaster()==&rBC
;
1620 if (bDying
&& (bObj1
|| bObj2
)) {
1621 // catch Dying, so AttrObj doesn't start broadcasting
1622 // about an alleged change of template
1623 if (bObj1
) aCon1
.pObj
=nullptr;
1624 if (bObj2
) aCon2
.pObj
=nullptr;
1627 if ( bObj1
|| bObj2
)
1629 bEdgeTrackUserDefined
= false;
1631 SdrTextObj::Notify(rBC
,rHint
);
1632 if (nNotifyingCount
!=0)return;
1636 const SdrHint
* pSdrHint
= ( rHint
.GetId() == SfxHintId::ThisIsAnSdrHint
? static_cast<const SdrHint
*>(&rHint
) : nullptr );
1638 if (bDataChg
) { // StyleSheet changed
1639 ImpSetAttrToEdgeInfo(); // when changing templates, copy values from Pool to aEdgeInfo
1642 (bObj1
&& aCon1
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
1643 (bObj2
&& aCon2
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
1644 (pSdrHint
&& pSdrHint
->GetKind()==SdrHintKind::ObjectRemoved
))
1646 // broadcasting only, if on the same page
1647 tools::Rectangle aBoundRect0
; if (m_pUserCall
!=nullptr) aBoundRect0
=GetCurrentBoundRect();
1648 ImpDirtyEdgeTrack();
1650 // only redraw here, object hasn't actually changed
1653 SendUserCall(SdrUserCallType::Resize
,aBoundRect0
);
1658 /** updates edges that are connected to the edges of this object
1659 as if the connected objects sent a repaint broadcast
1661 void SdrEdgeObj::Reformat()
1663 if( nullptr != aCon1
.pObj
)
1665 SfxHint
aHint( SfxHintId::DataChanged
);
1666 Notify( *const_cast<SfxBroadcaster
*>(aCon1
.pObj
->GetBroadcaster()), aHint
);
1669 if( nullptr != aCon2
.pObj
)
1671 SfxHint
aHint( SfxHintId::DataChanged
);
1672 Notify( *const_cast<SfxBroadcaster
*>(aCon2
.pObj
->GetBroadcaster()), aHint
);
1676 rtl::Reference
<SdrObject
> SdrEdgeObj::CloneSdrObject(SdrModel
& rTargetModel
) const
1678 return new SdrEdgeObj(rTargetModel
, *this);
1681 OUString
SdrEdgeObj::TakeObjNameSingul() const
1683 OUString
sName(SvxResId(STR_ObjNameSingulEDGE
));
1685 OUString
aName(GetName());
1686 if (!aName
.isEmpty())
1687 sName
+= " '" + aName
+ "'";
1691 OUString
SdrEdgeObj::TakeObjNamePlural() const
1693 return SvxResId(STR_ObjNamePluralEDGE
);
1696 basegfx::B2DPolyPolygon
SdrEdgeObj::TakeXorPoly() const
1698 basegfx::B2DPolyPolygon aPolyPolygon
;
1700 if (bEdgeTrackDirty
)
1702 const_cast<SdrEdgeObj
*>(this)->ImpRecalcEdgeTrack();
1707 aPolyPolygon
.append(pEdgeTrack
->getB2DPolygon());
1710 return aPolyPolygon
;
1713 void SdrEdgeObj::SetEdgeTrackPath( const basegfx::B2DPolyPolygon
& rPoly
)
1715 if ( !rPoly
.count() )
1717 bEdgeTrackDirty
= true;
1718 bEdgeTrackUserDefined
= false;
1722 *pEdgeTrack
= XPolygon( rPoly
.getB2DPolygon( 0 ) );
1723 bEdgeTrackDirty
= false;
1724 bEdgeTrackUserDefined
= true;
1726 // #i110629# also set aRect and maSnapeRect depending on pEdgeTrack
1727 const tools::Rectangle
aPolygonBounds(pEdgeTrack
->GetBoundRect());
1728 setRectangle(aPolygonBounds
);
1729 maSnapRect
= aPolygonBounds
;
1733 basegfx::B2DPolyPolygon
SdrEdgeObj::GetEdgeTrackPath() const
1735 basegfx::B2DPolyPolygon aPolyPolygon
;
1737 if (bEdgeTrackDirty
)
1738 const_cast<SdrEdgeObj
*>(this)->ImpRecalcEdgeTrack();
1740 aPolyPolygon
.append( pEdgeTrack
->getB2DPolygon() );
1742 return aPolyPolygon
;
1745 sal_uInt32
SdrEdgeObj::GetHdlCount() const
1747 SdrEdgeKind eKind
=GetObjectItem(SDRATTR_EDGEKIND
).GetValue();
1748 sal_uInt32
nHdlCnt(0);
1749 sal_uInt32
nPointCount(pEdgeTrack
->GetPointCount());
1754 if ((eKind
==SdrEdgeKind::OrthoLines
|| eKind
==SdrEdgeKind::Bezier
) && nPointCount
>= 4)
1756 sal_uInt32
nO1(aEdgeInfo
.nObj1Lines
> 0 ? aEdgeInfo
.nObj1Lines
- 1 : 0);
1757 sal_uInt32
nO2(aEdgeInfo
.nObj2Lines
> 0 ? aEdgeInfo
.nObj2Lines
- 1 : 0);
1758 sal_uInt32
nM(aEdgeInfo
.nMiddleLine
!= 0xFFFF ? 1 : 0);
1759 nHdlCnt
+= nO1
+ nO2
+ nM
;
1761 else if (eKind
==SdrEdgeKind::ThreeLines
&& nPointCount
== 4)
1763 if(GetConnectedNode(true))
1766 if(GetConnectedNode(false))
1774 void SdrEdgeObj::AddToHdlList(SdrHdlList
& rHdlList
) const
1776 sal_uInt32
nPointCount(pEdgeTrack
->GetPointCount());
1781 std::unique_ptr
<SdrHdl
> pHdl(new ImpEdgeHdl((*pEdgeTrack
)[0],SdrHdlKind::Poly
));
1782 if (aCon1
.pObj
!=nullptr && aCon1
.bBestVertex
) pHdl
->Set1PixMore();
1783 pHdl
->SetPointNum(0);
1784 rHdlList
.AddHdl(std::move(pHdl
));
1787 std::unique_ptr
<SdrHdl
> pHdl(new ImpEdgeHdl((*pEdgeTrack
)[sal_uInt16(nPointCount
-1)],SdrHdlKind::Poly
));
1788 if (aCon2
.pObj
!=nullptr && aCon2
.bBestVertex
) pHdl
->Set1PixMore();
1789 pHdl
->SetPointNum(1);
1790 rHdlList
.AddHdl(std::move(pHdl
));
1793 SdrEdgeKind eKind
=GetObjectItem(SDRATTR_EDGEKIND
).GetValue();
1794 if ((eKind
==SdrEdgeKind::OrthoLines
|| eKind
==SdrEdgeKind::Bezier
) && nPointCount
>= 4)
1796 sal_uInt32
nO1(aEdgeInfo
.nObj1Lines
> 0 ? aEdgeInfo
.nObj1Lines
- 1 : 0);
1797 sal_uInt32
nO2(aEdgeInfo
.nObj2Lines
> 0 ? aEdgeInfo
.nObj2Lines
- 1 : 0);
1798 sal_uInt32
nM(aEdgeInfo
.nMiddleLine
!= 0xFFFF ? 1 : 0);
1799 for(sal_uInt32 i
= 0; i
< (nO1
+ nO2
+ nM
); ++i
)
1802 sal_uInt32 nNum
= i
;
1803 std::unique_ptr
<ImpEdgeHdl
> pHdl(new ImpEdgeHdl(Point(),SdrHdlKind::Poly
));
1806 if (nNum
==0) pHdl
->SetLineCode(SdrEdgeLineCode::Obj1Line2
);
1807 if (nNum
==1) pHdl
->SetLineCode(SdrEdgeLineCode::Obj1Line3
);
1811 nPt
=nPointCount
-3-nNum
;
1812 if (nNum
==0) pHdl
->SetLineCode(SdrEdgeLineCode::Obj2Line2
);
1813 if (nNum
==1) pHdl
->SetLineCode(SdrEdgeLineCode::Obj2Line3
);
1817 nPt
=aEdgeInfo
.nMiddleLine
;
1818 pHdl
->SetLineCode(SdrEdgeLineCode::MiddleLine
);
1823 Point
aPos((*pEdgeTrack
)[static_cast<sal_uInt16
>(nPt
)]);
1824 aPos
+=(*pEdgeTrack
)[static_cast<sal_uInt16
>(nPt
)+1];
1825 aPos
.setX( aPos
.X() / 2 );
1826 aPos
.setY( aPos
.Y() / 2 );
1828 pHdl
->SetPointNum(i
+ 2);
1829 rHdlList
.AddHdl(std::move(pHdl
));
1833 else if (eKind
==SdrEdgeKind::ThreeLines
&& nPointCount
== 4)
1835 if(GetConnectedNode(true))
1837 Point
aPos((*pEdgeTrack
)[1]);
1838 std::unique_ptr
<ImpEdgeHdl
> pHdl(new ImpEdgeHdl(aPos
,SdrHdlKind::Poly
));
1839 pHdl
->SetLineCode(SdrEdgeLineCode::Obj1Line2
);
1840 pHdl
->SetPointNum(2);
1841 rHdlList
.AddHdl(std::move(pHdl
));
1843 if(GetConnectedNode(false))
1845 Point
aPos((*pEdgeTrack
)[2]);
1846 std::unique_ptr
<ImpEdgeHdl
> pHdl(new ImpEdgeHdl(aPos
,SdrHdlKind::Poly
));
1847 pHdl
->SetLineCode(SdrEdgeLineCode::Obj2Line2
);
1848 pHdl
->SetPointNum(3);
1849 rHdlList
.AddHdl(std::move(pHdl
));
1855 bool SdrEdgeObj::hasSpecialDrag() const
1860 rtl::Reference
<SdrObject
> SdrEdgeObj::getFullDragClone() const
1862 // use Clone operator
1863 rtl::Reference
<SdrEdgeObj
> pRetval
= SdrObject::Clone(*this, getSdrModelFromSdrObject());
1865 // copy connections for clone, SdrEdgeObj::operator= does not do this
1866 pRetval
->ConnectToNode(true, GetConnectedNode(true));
1867 pRetval
->ConnectToNode(false, GetConnectedNode(false));
1872 bool SdrEdgeObj::beginSpecialDrag(SdrDragStat
& rDrag
) const
1877 rDrag
.SetEndDragChangesAttributes(true);
1879 if(rDrag
.GetHdl()->GetPointNum() < 2)
1887 bool SdrEdgeObj::applySpecialDrag(SdrDragStat
& rDragStat
)
1889 SdrEdgeObj
* pOriginalEdge
= dynamic_cast< SdrEdgeObj
* >(rDragStat
.GetHdl()->GetObj());
1890 const bool bOriginalEdgeModified(pOriginalEdge
== this);
1892 if(!bOriginalEdgeModified
&& pOriginalEdge
)
1894 // copy connections when clone is modified. This is needed because
1895 // as preparation to this modification the data from the original object
1896 // was copied to the clone using the operator=. As can be seen there,
1897 // that operator does not copy the connections (for good reason)
1898 ConnectToNode(true, pOriginalEdge
->GetConnection(true).GetObject());
1899 ConnectToNode(false, pOriginalEdge
->GetConnection(false).GetObject());
1902 if(rDragStat
.GetHdl()->GetPointNum() < 2)
1904 // start or end point connector drag
1905 const bool bDragA(0 == rDragStat
.GetHdl()->GetPointNum());
1906 const Point
aPointNow(rDragStat
.GetNow());
1908 rDragStat
.SetEndDragChangesGeoAndAttributes(true);
1910 if(rDragStat
.GetPageView())
1912 SdrObjConnection
* pDraggedOne(bDragA
? &aCon1
: &aCon2
);
1915 DisconnectFromNode(bDragA
);
1917 // look for new connection
1918 ImpFindConnector(aPointNow
, *rDragStat
.GetPageView(), *pDraggedOne
, pOriginalEdge
, nullptr, &rDragStat
);
1920 if(pDraggedOne
->pObj
)
1922 // if found, officially connect to it; ImpFindConnector only
1924 SdrObject
* pNewConnection
= pDraggedOne
->pObj
;
1925 pDraggedOne
->pObj
= nullptr;
1926 ConnectToNode(bDragA
, pNewConnection
);
1929 if(rDragStat
.GetView() && !bOriginalEdgeModified
)
1931 // show IA helper, but only do this during IA, so not when the original
1932 // Edge gets modified in the last call
1933 rDragStat
.GetView()->SetConnectMarker(*pDraggedOne
);
1939 // change pEdgeTrack to modified position
1942 (*pEdgeTrack
)[0] = aPointNow
;
1946 (*pEdgeTrack
)[sal_uInt16(pEdgeTrack
->GetPointCount()-1)] = aPointNow
;
1950 // reset edge info's offsets, this is an end point drag
1951 aEdgeInfo
.aObj1Line2
= Point();
1952 aEdgeInfo
.aObj1Line3
= Point();
1953 aEdgeInfo
.aObj2Line2
= Point();
1954 aEdgeInfo
.aObj2Line3
= Point();
1955 aEdgeInfo
.aMiddleLine
= Point();
1959 // control point connector drag
1960 const ImpEdgeHdl
* pEdgeHdl
= static_cast<const ImpEdgeHdl
*>(rDragStat
.GetHdl());
1961 const SdrEdgeLineCode eLineCode
= pEdgeHdl
->GetLineCode();
1962 const Point
aDist(rDragStat
.GetNow() - rDragStat
.GetStart());
1963 sal_Int32
nDist(pEdgeHdl
->IsHorzDrag() ? aDist
.X() : aDist
.Y());
1965 nDist
+= aEdgeInfo
.ImpGetLineOffset(eLineCode
, *pEdgeTrack
);
1966 aEdgeInfo
.ImpSetLineOffset(eLineCode
, *pEdgeTrack
, nDist
);
1969 // force recalculation of EdgeTrack
1970 *pEdgeTrack
= ImpCalcEdgeTrack(*pEdgeTrack
, aCon1
, aCon2
, &aEdgeInfo
);
1971 bEdgeTrackDirty
=false;
1973 // save EdgeInfos and mark object as user modified
1974 ImpSetEdgeInfoToAttr();
1975 bEdgeTrackUserDefined
= false;
1977 SetBoundAndSnapRectsDirty();
1979 if(bOriginalEdgeModified
&& rDragStat
.GetView())
1981 // hide connect marker helper again when original gets changed.
1982 // This happens at the end of the interaction
1983 rDragStat
.GetView()->HideConnectMarker();
1989 OUString
SdrEdgeObj::getSpecialDragComment(const SdrDragStat
& rDrag
) const
1991 const bool bCreateComment(rDrag
.GetView() && this == rDrag
.GetView()->GetCreateObj());
1999 return ImpGetDescriptionStr(STR_DragEdgeTail
);
2004 basegfx::B2DPolygon
SdrEdgeObj::ImplAddConnectorOverlay(const SdrDragMethod
& rDragMethod
, bool bTail1
, bool bTail2
, bool bDetail
) const
2006 basegfx::B2DPolygon aResult
;
2010 SdrObjConnection
aMyCon1(aCon1
);
2011 SdrObjConnection
aMyCon2(aCon2
);
2015 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aMyCon1
.aObjOfs
.X(), aMyCon1
.aObjOfs
.Y()));
2016 aMyCon1
.aObjOfs
.setX( basegfx::fround(aTemp
.getX()) );
2017 aMyCon1
.aObjOfs
.setY( basegfx::fround(aTemp
.getY()) );
2022 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aMyCon2
.aObjOfs
.X(), aMyCon2
.aObjOfs
.Y()));
2023 aMyCon2
.aObjOfs
.setX( basegfx::fround(aTemp
.getX()) );
2024 aMyCon2
.aObjOfs
.setY( basegfx::fround(aTemp
.getY()) );
2027 SdrEdgeInfoRec
aInfo(aEdgeInfo
);
2028 XPolygon
aXP(ImpCalcEdgeTrack(*pEdgeTrack
, aMyCon1
, aMyCon2
, &aInfo
));
2030 if(aXP
.GetPointCount())
2032 aResult
= aXP
.getB2DPolygon();
2037 Point
aPt1((*pEdgeTrack
)[0]);
2038 Point
aPt2((*pEdgeTrack
)[sal_uInt16(pEdgeTrack
->GetPointCount() - 1)]);
2040 if (aCon1
.pObj
&& (aCon1
.bBestConn
|| aCon1
.bBestVertex
))
2041 aPt1
= aCon1
.pObj
->GetSnapRect().Center();
2043 if (aCon2
.pObj
&& (aCon2
.bBestConn
|| aCon2
.bBestVertex
))
2044 aPt2
= aCon2
.pObj
->GetSnapRect().Center();
2048 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aPt1
.X(), aPt1
.Y()));
2049 aPt1
.setX( basegfx::fround(aTemp
.getX()) );
2050 aPt1
.setY( basegfx::fround(aTemp
.getY()) );
2055 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aPt2
.X(), aPt2
.Y()));
2056 aPt2
.setX( basegfx::fround(aTemp
.getX()) );
2057 aPt2
.setY( basegfx::fround(aTemp
.getY()) );
2060 aResult
.append(basegfx::B2DPoint(aPt1
.X(), aPt1
.Y()));
2061 aResult
.append(basegfx::B2DPoint(aPt2
.X(), aPt2
.Y()));
2067 bool SdrEdgeObj::BegCreate(SdrDragStat
& rDragStat
)
2069 rDragStat
.SetNoSnap();
2070 pEdgeTrack
->SetPointCount(2);
2071 (*pEdgeTrack
)[0]=rDragStat
.GetStart();
2072 (*pEdgeTrack
)[1]=rDragStat
.GetNow();
2073 if (rDragStat
.GetPageView()!=nullptr) {
2074 ImpFindConnector(rDragStat
.GetStart(),*rDragStat
.GetPageView(),aCon1
,this);
2075 ConnectToNode(true,aCon1
.pObj
);
2077 *pEdgeTrack
=ImpCalcEdgeTrack(*pEdgeTrack
,aCon1
,aCon2
,&aEdgeInfo
);
2081 bool SdrEdgeObj::MovCreate(SdrDragStat
& rDragStat
)
2083 sal_uInt16 nMax
=pEdgeTrack
->GetPointCount();
2084 (*pEdgeTrack
)[nMax
-1]=rDragStat
.GetNow();
2085 if (rDragStat
.GetPageView()!=nullptr) {
2086 ImpFindConnector(rDragStat
.GetNow(),*rDragStat
.GetPageView(),aCon2
,this);
2087 rDragStat
.GetView()->SetConnectMarker(aCon2
);
2089 SetBoundRectDirty();
2090 m_bSnapRectDirty
=true;
2091 ConnectToNode(false,aCon2
.pObj
);
2092 *pEdgeTrack
=ImpCalcEdgeTrack(*pEdgeTrack
,aCon1
,aCon2
,&aEdgeInfo
);
2093 bEdgeTrackDirty
=false;
2097 bool SdrEdgeObj::EndCreate(SdrDragStat
& rDragStat
, SdrCreateCmd eCmd
)
2099 bool bOk
=(eCmd
==SdrCreateCmd::ForceEnd
|| rDragStat
.GetPointCount()>=2);
2101 ConnectToNode(true,aCon1
.pObj
);
2102 ConnectToNode(false,aCon2
.pObj
);
2103 if (rDragStat
.GetView()!=nullptr) {
2104 rDragStat
.GetView()->HideConnectMarker();
2106 ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
2108 SetBoundAndSnapRectsDirty();
2112 bool SdrEdgeObj::BckCreate(SdrDragStat
& rDragStat
)
2114 if (rDragStat
.GetView()!=nullptr) {
2115 rDragStat
.GetView()->HideConnectMarker();
2120 void SdrEdgeObj::BrkCreate(SdrDragStat
& rDragStat
)
2122 if (rDragStat
.GetView()!=nullptr) {
2123 rDragStat
.GetView()->HideConnectMarker();
2127 basegfx::B2DPolyPolygon
SdrEdgeObj::TakeCreatePoly(const SdrDragStat
& /*rStatDrag*/) const
2129 basegfx::B2DPolyPolygon aRetval
;
2130 aRetval
.append(pEdgeTrack
->getB2DPolygon());
2134 PointerStyle
SdrEdgeObj::GetCreatePointer() const
2136 return PointerStyle::DrawConnect
;
2139 bool SdrEdgeObj::ImpFindConnector(const Point
& rPt
, const SdrPageView
& rPV
, SdrObjConnection
& rCon
, const SdrEdgeObj
* pThis
, OutputDevice
* pOut
, SdrDragStat
* pDragStat
)
2142 if (pOut
==nullptr) pOut
=rPV
.GetView().GetFirstOutputDevice();
2143 if (pOut
==nullptr) return false;
2144 SdrObjList
* pOL
=rPV
.GetObjList();
2145 const SdrLayerIDSet
& rVisLayer
=rPV
.GetVisibleLayers();
2146 // sensitive area of connectors is twice as large as the one of the handles
2147 sal_uInt16 nMarkHdSiz
=rPV
.GetView().GetMarkHdlSizePixel();
2148 Size
aHalfConSiz(nMarkHdSiz
,nMarkHdSiz
);
2149 if (comphelper::LibreOfficeKit::isActive() && pOut
->GetMapMode().GetMapUnit() == MapUnit::Map100thMM
)
2150 aHalfConSiz
=pOut
->PixelToLogic(aHalfConSiz
, MapMode(MapUnit::Map100thMM
));
2152 aHalfConSiz
=pOut
->PixelToLogic(aHalfConSiz
);
2153 tools::Rectangle
aMouseRect(rPt
,rPt
);
2154 aMouseRect
.AdjustLeft( -(aHalfConSiz
.Width()) );
2155 aMouseRect
.AdjustTop( -(aHalfConSiz
.Height()) );
2156 aMouseRect
.AdjustRight(aHalfConSiz
.Width() );
2157 aMouseRect
.AdjustBottom(aHalfConSiz
.Height() );
2158 double fBoundHitTol
=static_cast<double>(aHalfConSiz
.Width())/2; if (fBoundHitTol
==0.0) fBoundHitTol
=1.0;
2159 size_t no
=pOL
->GetObjCount();
2161 SdrObjConnection aTestCon
;
2162 bool bTiledRendering
= comphelper::LibreOfficeKit::isActive();
2163 bool bHasRequestedOrdNum
= false;
2164 sal_Int32 requestedOrdNum
= -1;
2166 if (bTiledRendering
&& pDragStat
)
2168 auto& glueOptions
= pDragStat
->GetGlueOptions();
2169 if (glueOptions
.objectOrdNum
!= -1)
2171 requestedOrdNum
= glueOptions
.objectOrdNum
;
2172 bHasRequestedOrdNum
= true;
2176 while (no
>0 && !bFnd
) {
2177 // issue: group objects on different layers return LayerID=0!
2179 SdrObject
* pObj
=pOL
->GetObj(no
);
2180 if (bHasRequestedOrdNum
)
2182 if (pObj
->GetOrdNumDirect() != static_cast<sal_uInt32
>(requestedOrdNum
))
2185 if (rVisLayer
.IsSet(pObj
->GetLayer()) && pObj
->IsVisible() && // only visible objects
2186 (pThis
==nullptr || pObj
!=static_cast<SdrObject
const *>(pThis
))) // don't connect it to itself
2188 tools::Rectangle
aObjBound(pObj
->GetCurrentBoundRect());
2189 if (aObjBound
.Overlaps(aMouseRect
)) {
2190 aTestCon
.ResetVars();
2191 bool bEdge
=dynamic_cast<const SdrEdgeObj
*>(pObj
) != nullptr; // no BestCon for Edge
2192 // User-defined connectors have absolute priority.
2193 // After those come Vertex, Corner and center (Best), all prioritized equally.
2194 // Finally, a HitTest for the object.
2195 const SdrGluePointList
* pGPL
=pObj
->GetGluePointList();
2196 sal_uInt16 nGluePointCnt
=pGPL
==nullptr ? 0 : pGPL
->GetCount();
2197 sal_uInt16 nGesAnz
=nGluePointCnt
+9;
2198 bool bUserFnd
= false;
2199 sal_uIntPtr nBestDist
=0xFFFFFFFF;
2200 for (sal_uInt16 i
=0; i
<nGesAnz
; i
++)
2202 bool bUser
=i
<nGluePointCnt
;
2203 bool bVertex
=i
>=nGluePointCnt
+0 && i
<nGluePointCnt
+4;
2204 bool bCorner
=i
>=nGluePointCnt
+4 && i
<nGluePointCnt
+8;
2205 bool bCenter
=i
==nGluePointCnt
+8;
2208 sal_uInt16 nConNum
=i
;
2210 const SdrGluePoint
& rGP
=(*pGPL
)[nConNum
];
2211 aConPos
=rGP
.GetAbsolutePos(*pObj
);
2212 nConNum
=rGP
.GetId();
2214 } else if (bVertex
&& !bUserFnd
) {
2215 nConNum
=nConNum
-nGluePointCnt
;
2216 SdrGluePoint
aPt(pObj
->GetVertexGluePoint(nConNum
));
2217 aConPos
=aPt
.GetAbsolutePos(*pObj
);
2219 } else if (bCorner
&& !bUserFnd
) {
2220 nConNum
-=nGluePointCnt
+4;
2223 else if (bCenter
&& !bUserFnd
&& !bEdge
)
2225 // Suppress default connect at object center
2226 if(!pThis
|| !pThis
->GetSuppressDefaultConnect())
2230 aConPos
=aObjBound
.Center();
2234 if (bOk
&& aMouseRect
.Contains(aConPos
)) {
2235 if (bUser
) bUserFnd
= true;
2237 sal_uIntPtr nDist
=static_cast<sal_uIntPtr
>(std::abs(aConPos
.X()-rPt
.X()))+static_cast<sal_uIntPtr
>(std::abs(aConPos
.Y()-rPt
.Y()));
2238 if (nDist
<nBestDist
) {
2241 aTestCon
.nConId
=nConNum
;
2242 aTestCon
.bAutoCorner
=bCorner
;
2243 aTestCon
.bAutoVertex
=bVertex
;
2244 aTestCon
.bBestConn
=false; // bCenter;
2245 aTestCon
.bBestVertex
=bCenter
;
2249 // if no connector is hit, try HitTest again, for BestConnector (=bCenter)
2252 SdrObjectPrimitiveHit(*pObj
, rPt
, {fBoundHitTol
, fBoundHitTol
}, rPV
, &rVisLayer
, false))
2254 // Suppress default connect at object inside bound
2255 if(!pThis
|| !pThis
->GetSuppressDefaultConnect())
2259 aTestCon
.bBestConn
=true;
2263 aMouseRect
.AdjustLeft( -fBoundHitTol
);
2264 aMouseRect
.AdjustTop( -fBoundHitTol
);
2265 aMouseRect
.AdjustRight(fBoundHitTol
);
2266 aMouseRect
.AdjustBottom(fBoundHitTol
);
2276 void SdrEdgeObj::NbcSetSnapRect(const tools::Rectangle
& rRect
)
2278 const tools::Rectangle
aOld(GetSnapRect());
2283 if (getRectangle().IsEmpty() && 0 == pEdgeTrack
->GetPointCount())
2285 // #i110629# When initializing, do not scale on empty Rectangle; this
2286 // will mirror the underlying text object (!)
2287 setRectangle(rRect
);
2292 tools::Long nMulX
= rRect
.Right() - rRect
.Left();
2293 tools::Long nDivX
= aOld
.Right() - aOld
.Left();
2294 tools::Long nMulY
= rRect
.Bottom() - rRect
.Top();
2295 tools::Long nDivY
= aOld
.Bottom() - aOld
.Top();
2296 if ( nDivX
== 0 ) { nMulX
= 1; nDivX
= 1; }
2297 if ( nDivY
== 0 ) { nMulY
= 1; nDivY
= 1; }
2298 Fraction
aX(nMulX
, nDivX
);
2299 Fraction
aY(nMulY
, nDivY
);
2300 NbcResize(aOld
.TopLeft(), aX
, aY
);
2301 NbcMove(Size(rRect
.Left() - aOld
.Left(), rRect
.Top() - aOld
.Top()));
2305 void SdrEdgeObj::NbcMove(const Size
& rSiz
)
2307 SdrTextObj::NbcMove(rSiz
);
2308 MoveXPoly(*pEdgeTrack
,rSiz
);
2311 void SdrEdgeObj::NbcResize(const Point
& rRefPnt
, const Fraction
& aXFact
, const Fraction
& aYFact
)
2313 SdrTextObj::NbcResize(rRefPnt
,aXFact
,aXFact
);
2314 ResizeXPoly(*pEdgeTrack
,rRefPnt
,aXFact
,aYFact
);
2316 // if resize is not from paste, forget user distances
2317 if (!getSdrModelFromSdrObject().IsPasteResize())
2319 aEdgeInfo
.aObj1Line2
= Point();
2320 aEdgeInfo
.aObj1Line3
= Point();
2321 aEdgeInfo
.aObj2Line2
= Point();
2322 aEdgeInfo
.aObj2Line3
= Point();
2323 aEdgeInfo
.aMiddleLine
= Point();
2327 // #i54102# added rotation support
2328 void SdrEdgeObj::NbcRotate(const Point
& rRef
, Degree100 nAngle
, double sn
, double cs
)
2330 if(bEdgeTrackUserDefined
)
2332 // #i120437# special handling when track is imported, apply
2333 // transformation directly to imported track.
2334 SdrTextObj::NbcRotate(rRef
, nAngle
, sn
, cs
);
2335 RotateXPoly(*pEdgeTrack
, rRef
, sn
, cs
);
2339 // handle start and end point if not connected
2340 const bool bCon1(nullptr != aCon1
.pObj
&& aCon1
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2341 const bool bCon2(nullptr != aCon2
.pObj
&& aCon2
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2343 if(!bCon1
&& pEdgeTrack
)
2345 RotatePoint((*pEdgeTrack
)[0],rRef
,sn
,cs
);
2346 ImpDirtyEdgeTrack();
2349 if(!bCon2
&& pEdgeTrack
)
2351 sal_uInt16 nPointCount
= pEdgeTrack
->GetPointCount();
2352 RotatePoint((*pEdgeTrack
)[sal_uInt16(nPointCount
-1)],rRef
,sn
,cs
);
2353 ImpDirtyEdgeTrack();
2358 // #i54102# added mirror support
2359 void SdrEdgeObj::NbcMirror(const Point
& rRef1
, const Point
& rRef2
)
2361 if(bEdgeTrackUserDefined
)
2363 // #i120437# special handling when track is imported, apply
2364 // transformation directly to imported track.
2365 SdrTextObj::NbcMirror(rRef1
, rRef2
);
2366 MirrorXPoly(*pEdgeTrack
, rRef1
, rRef2
);
2370 // handle start and end point if not connected
2371 const bool bCon1(nullptr != aCon1
.pObj
&& aCon1
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2372 const bool bCon2(nullptr != aCon2
.pObj
&& aCon2
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2374 if(!bCon1
&& pEdgeTrack
)
2376 MirrorPoint((*pEdgeTrack
)[0],rRef1
,rRef2
);
2377 ImpDirtyEdgeTrack();
2380 if(!bCon2
&& pEdgeTrack
)
2382 sal_uInt16 nPointCount
= pEdgeTrack
->GetPointCount();
2383 MirrorPoint((*pEdgeTrack
)[sal_uInt16(nPointCount
-1)],rRef1
,rRef2
);
2384 ImpDirtyEdgeTrack();
2389 // #i54102# added shear support
2390 void SdrEdgeObj::NbcShear(const Point
& rRef
, Degree100 nAngle
, double tn
, bool bVShear
)
2392 if(bEdgeTrackUserDefined
)
2394 // #i120437# special handling when track is imported, apply
2395 // transformation directly to imported track.
2396 SdrTextObj::NbcShear(rRef
, nAngle
, tn
, bVShear
);
2397 ShearXPoly(*pEdgeTrack
, rRef
, tn
, bVShear
);
2401 // handle start and end point if not connected
2402 const bool bCon1(nullptr != aCon1
.pObj
&& aCon1
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2403 const bool bCon2(nullptr != aCon2
.pObj
&& aCon2
.pObj
->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2405 if(!bCon1
&& pEdgeTrack
)
2407 ShearPoint((*pEdgeTrack
)[0],rRef
,tn
,bVShear
);
2408 ImpDirtyEdgeTrack();
2411 if(!bCon2
&& pEdgeTrack
)
2413 sal_uInt16 nPointCount
= pEdgeTrack
->GetPointCount();
2414 ShearPoint((*pEdgeTrack
)[sal_uInt16(nPointCount
-1)],rRef
,tn
,bVShear
);
2415 ImpDirtyEdgeTrack();
2420 rtl::Reference
<SdrObject
> SdrEdgeObj::DoConvertToPolyObj(bool bBezier
, bool bAddText
) const
2422 basegfx::B2DPolyPolygon aPolyPolygon
;
2423 aPolyPolygon
.append(pEdgeTrack
->getB2DPolygon());
2424 rtl::Reference
<SdrObject
> pRet
= ImpConvertMakeObj(aPolyPolygon
, false, bBezier
);
2428 pRet
= ImpConvertAddText(std::move(pRet
), bBezier
);
2434 sal_uInt32
SdrEdgeObj::GetSnapPointCount() const
2439 Point
SdrEdgeObj::GetSnapPoint(sal_uInt32 i
) const
2441 const_cast<SdrEdgeObj
*>(this)->ImpUndirtyEdgeTrack();
2442 sal_uInt16 nCount
=pEdgeTrack
->GetPointCount();
2443 if (i
==0) return (*pEdgeTrack
)[0];
2444 else return (*pEdgeTrack
)[nCount
-1];
2447 bool SdrEdgeObj::IsPolyObj() const
2452 sal_uInt32
SdrEdgeObj::GetPointCount() const
2457 Point
SdrEdgeObj::GetPoint(sal_uInt32 i
) const
2459 const_cast<SdrEdgeObj
*>(this)->ImpUndirtyEdgeTrack();
2460 sal_uInt16 nCount
=pEdgeTrack
->GetPointCount();
2462 return (*pEdgeTrack
)[0];
2464 return (*pEdgeTrack
)[nCount
-1];
2467 void SdrEdgeObj::NbcSetPoint(const Point
& rPnt
, sal_uInt32 i
)
2469 // TODO: Need an implementation to connect differently.
2470 ImpUndirtyEdgeTrack();
2471 sal_uInt16 nCount
=pEdgeTrack
->GetPointCount();
2473 (*pEdgeTrack
)[0]=rPnt
;
2475 (*pEdgeTrack
)[nCount
-1]=rPnt
;
2476 SetEdgeTrackDirty();
2477 SetBoundAndSnapRectsDirty();
2480 SdrEdgeObjGeoData::SdrEdgeObjGeoData()
2481 : pEdgeTrack(std::in_place
)
2482 , bEdgeTrackDirty(false)
2483 , bEdgeTrackUserDefined(false)
2487 SdrEdgeObjGeoData::~SdrEdgeObjGeoData()
2491 std::unique_ptr
<SdrObjGeoData
> SdrEdgeObj::NewGeoData() const
2493 return std::make_unique
<SdrEdgeObjGeoData
>();
2496 void SdrEdgeObj::SaveGeoData(SdrObjGeoData
& rGeo
) const
2498 SdrTextObj::SaveGeoData(rGeo
);
2499 SdrEdgeObjGeoData
& rEGeo
=static_cast<SdrEdgeObjGeoData
&>(rGeo
);
2502 *rEGeo
.pEdgeTrack
=*pEdgeTrack
;
2503 rEGeo
.bEdgeTrackDirty
=bEdgeTrackDirty
;
2504 rEGeo
.bEdgeTrackUserDefined
=bEdgeTrackUserDefined
;
2505 rEGeo
.aEdgeInfo
=aEdgeInfo
;
2508 void SdrEdgeObj::RestoreGeoData(const SdrObjGeoData
& rGeo
)
2510 SdrTextObj::RestoreGeoData(rGeo
);
2511 const SdrEdgeObjGeoData
& rEGeo
=static_cast<const SdrEdgeObjGeoData
&>(rGeo
);
2512 if (aCon1
.pObj
!=rEGeo
.aCon1
.pObj
) {
2513 if (aCon1
.pObj
!=nullptr) aCon1
.pObj
->RemoveListener(*this);
2515 if (aCon1
.pObj
!=nullptr) aCon1
.pObj
->AddListener(*this);
2520 if (aCon2
.pObj
!=rEGeo
.aCon2
.pObj
) {
2521 if (aCon2
.pObj
!=nullptr) aCon2
.pObj
->RemoveListener(*this);
2523 if (aCon2
.pObj
!=nullptr) aCon2
.pObj
->AddListener(*this);
2528 *pEdgeTrack
=*rEGeo
.pEdgeTrack
;
2529 bEdgeTrackDirty
=rEGeo
.bEdgeTrackDirty
;
2530 bEdgeTrackUserDefined
=rEGeo
.bEdgeTrackUserDefined
;
2531 aEdgeInfo
=rEGeo
.aEdgeInfo
;
2534 Point
SdrEdgeObj::GetTailPoint( bool bTail
) const
2536 if( pEdgeTrack
&& pEdgeTrack
->GetPointCount()!=0)
2538 const XPolygon
& rTrack0
= *pEdgeTrack
;
2545 const sal_uInt16 nSiz
= rTrack0
.GetPointCount() - 1;
2546 return rTrack0
[nSiz
];
2552 return getOutRectangle().TopLeft();
2554 return getOutRectangle().BottomRight();
2559 void SdrEdgeObj::SetTailPoint( bool bTail
, const Point
& rPt
)
2561 ImpSetTailPoint( bTail
, rPt
);
2565 /** this method is used by the api to set a gluepoint for a connection
2566 nId == -1 : The best default point is automatically chosen
2567 0 <= nId <= 3 : One of the default points is chosen
2568 nId >= 4 : A user defined gluepoint is chosen
2570 void SdrEdgeObj::setGluePointIndex( bool bTail
, sal_Int32 nIndex
/* = -1 */ )
2572 SdrObjConnection
& rConn1
= GetConnection( bTail
);
2574 rConn1
.SetAutoVertex( nIndex
>= 0 && nIndex
<= 3 );
2575 rConn1
.SetBestConnection( nIndex
< 0 );
2576 rConn1
.SetBestVertex( nIndex
< 0 );
2580 nIndex
-= 3; // the start api index is 0, whereas the implementation in svx starts from 1
2582 // for user defined gluepoints we have
2583 // to get the id for this index first
2584 const SdrGluePointList
* pList
= rConn1
.GetObject() ? rConn1
.GetObject()->GetGluePointList() : nullptr;
2585 if( pList
== nullptr || SDRGLUEPOINT_NOTFOUND
== pList
->FindGluePoint(static_cast<sal_uInt16
>(nIndex
)) )
2588 else if( nIndex
< 0 )
2593 rConn1
.SetConnectorId( static_cast<sal_uInt16
>(nIndex
) );
2596 SetBoundAndSnapRectsDirty();
2597 ImpRecalcEdgeTrack();
2600 /** this method is used by the api to return a gluepoint id for a connection.
2601 See setGluePointId for possible return values */
2602 sal_Int32
SdrEdgeObj::getGluePointIndex( bool bTail
)
2604 SdrObjConnection
& rConn1
= GetConnection( bTail
);
2606 if( !rConn1
.IsBestConnection() )
2608 nId
= rConn1
.GetConnectorId();
2609 if( !rConn1
.IsAutoVertex() )
2610 nId
+= 3; // the start api index is 0, whereas the implementation in svx starts from 1
2615 // Implementation was missing; edge track needs to be invalidated additionally.
2616 void SdrEdgeObj::NbcSetAnchorPos(const Point
& rPnt
)
2618 // call parent functionality
2619 SdrTextObj::NbcSetAnchorPos(rPnt
);
2621 // Additionally, invalidate edge track
2622 ImpDirtyEdgeTrack();
2625 bool SdrEdgeObj::TRGetBaseGeometry(basegfx::B2DHomMatrix
& rMatrix
, basegfx::B2DPolyPolygon
& rPolyPolygon
) const
2627 // use base method from SdrObject, it's not rotatable and
2628 // a call to GetSnapRect() is used. That's what we need for Connector.
2629 return SdrObject::TRGetBaseGeometry(rMatrix
, rPolyPolygon
);
2632 void SdrEdgeObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::B2DPolyPolygon
& rPolyPolygon
)
2634 // where appropriate take care for existing connections. For now, just use the
2635 // implementation from SdrObject.
2636 SdrObject::TRSetBaseGeometry(rMatrix
, rPolyPolygon
);
2639 // for geometry access
2640 ::basegfx::B2DPolygon
SdrEdgeObj::getEdgeTrack() const
2644 const_cast< SdrEdgeObj
* >(this)->ImpRecalcEdgeTrack();
2649 return pEdgeTrack
->getB2DPolygon();
2653 return ::basegfx::B2DPolygon();
2657 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */