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 .
21 #include <svx/svdoedge.hxx>
22 #include <svx/xpool.hxx>
23 #include <svx/xpoly.hxx>
24 #include <svx/svdattrx.hxx>
25 #include <svx/svdpool.hxx>
26 #include <svx/svdmodel.hxx>
27 #include <svx/svdpage.hxx>
28 #include <svx/svdpagv.hxx>
29 #include <svx/svdview.hxx>
30 #include <svx/svddrag.hxx>
31 #include <svx/svddrgv.hxx>
32 #include "svddrgm1.hxx"
33 #include <svx/svdhdl.hxx>
34 #include <svx/svdtrans.hxx>
35 #include <svx/svdetc.hxx>
36 #include "svx/svdglob.hxx" // StringCache
37 #include "svx/svdstr.hrc" // the object's name
38 #include <svl/style.hxx>
39 #include <svl/smplhint.hxx>
40 #include <editeng/eeitem.hxx>
41 #include <svx/sdr/properties/connectorproperties.hxx>
42 #include <svx/sdr/contact/viewcontactofsdredgeobj.hxx>
43 #include <basegfx/polygon/b2dpolygon.hxx>
44 #include <basegfx/polygon/b2dpolygontools.hxx>
45 #include <basegfx/matrix/b2dhommatrix.hxx>
46 #include <svx/sdrhittesthelper.hxx>
48 ////////////////////////////////////////////////////////////////////////////////////////////////////
50 SdrObjConnection::~SdrObjConnection()
54 void SdrObjConnection::ResetVars()
64 bAutoVertex
=sal_False
;
65 bAutoCorner
=sal_False
;
68 bool SdrObjConnection::TakeGluePoint(SdrGluePoint
& rGP
, bool bSetAbsPos
) const
71 if (pObj
!=NULL
) { // one object has to be docked already!
73 rGP
=pObj
->GetVertexGluePoint(nConId
);
75 } else if (bAutoCorner
) {
76 rGP
=pObj
->GetCornerGluePoint(nConId
);
79 const SdrGluePointList
* pGPL
=pObj
->GetGluePointList();
81 sal_uInt16 nNum
=pGPL
->FindGluePoint(nConId
);
82 if (nNum
!=SDRGLUEPOINT_NOTFOUND
) {
89 if (bRet
&& bSetAbsPos
) {
90 Point
aPt(rGP
.GetAbsolutePos(*pObj
));
97 Point
& SdrEdgeInfoRec::ImpGetLineVersatzPoint(SdrEdgeLineCode eLineCode
)
100 case OBJ1LINE2
: return aObj1Line2
;
101 case OBJ1LINE3
: return aObj1Line3
;
102 case OBJ2LINE2
: return aObj2Line2
;
103 case OBJ2LINE3
: return aObj2Line3
;
104 case MIDDLELINE
: return aMiddleLine
;
109 sal_uInt16
SdrEdgeInfoRec::ImpGetPolyIdx(SdrEdgeLineCode eLineCode
, const XPolygon
& rXP
) const
112 case OBJ1LINE2
: return 1;
113 case OBJ1LINE3
: return 2;
114 case OBJ2LINE2
: return rXP
.GetPointCount()-3;
115 case OBJ2LINE3
: return rXP
.GetPointCount()-4;
116 case MIDDLELINE
: return nMiddleLine
;
121 bool SdrEdgeInfoRec::ImpIsHorzLine(SdrEdgeLineCode eLineCode
, const XPolygon
& rXP
) const
123 sal_uInt16 nIdx
=ImpGetPolyIdx(eLineCode
,rXP
);
124 bool bHorz
=nAngle1
==0 || nAngle1
==18000;
125 if (eLineCode
==OBJ2LINE2
|| eLineCode
==OBJ2LINE3
) {
126 nIdx
=rXP
.GetPointCount()-nIdx
;
127 bHorz
=nAngle2
==0 || nAngle2
==18000;
129 if ((nIdx
& 1)==1) bHorz
=!bHorz
;
133 void SdrEdgeInfoRec::ImpSetLineVersatz(SdrEdgeLineCode eLineCode
, const XPolygon
& rXP
, long nVal
)
135 Point
& rPt
=ImpGetLineVersatzPoint(eLineCode
);
136 if (ImpIsHorzLine(eLineCode
,rXP
)) rPt
.Y()=nVal
;
140 long SdrEdgeInfoRec::ImpGetLineVersatz(SdrEdgeLineCode eLineCode
, const XPolygon
& rXP
) const
142 const Point
& rPt
=ImpGetLineVersatzPoint(eLineCode
);
143 if (ImpIsHorzLine(eLineCode
,rXP
)) return rPt
.Y();
147 //////////////////////////////////////////////////////////////////////////////
148 // BaseProperties section
150 sdr::properties::BaseProperties
* SdrEdgeObj::CreateObjectSpecificProperties()
152 return new sdr::properties::ConnectorProperties(*this);
155 //////////////////////////////////////////////////////////////////////////////
156 // DrawContact section
158 sdr::contact::ViewContact
* SdrEdgeObj::CreateObjectSpecificViewContact()
160 return new sdr::contact::ViewContactOfSdrEdgeObj(*this);
163 //////////////////////////////////////////////////////////////////////////////
165 TYPEINIT1(SdrEdgeObj
,SdrTextObj
);
167 SdrEdgeObj::SdrEdgeObj()
170 bEdgeTrackDirty(sal_False
),
171 bEdgeTrackUserDefined(sal_False
),
172 // Default is to allow default connects
173 mbSuppressDefaultConnect(sal_False
),
174 mbBoundRectCalculationRunning(sal_False
),
177 bClosedObj
=sal_False
;
179 pEdgeTrack
=new XPolygon
;
183 SdrEdgeObj::~SdrEdgeObj()
185 DisconnectFromNode(sal_True
);
186 DisconnectFromNode(sal_False
);
190 void SdrEdgeObj::ImpSetAttrToEdgeInfo()
192 const SfxItemSet
& rSet
= GetObjectItemSet();
193 SdrEdgeKind eKind
= ((SdrEdgeKindItem
&)(rSet
.Get(SDRATTR_EDGEKIND
))).GetValue();
194 sal_Int32 nVal1
= ((SdrEdgeLine1DeltaItem
&)rSet
.Get(SDRATTR_EDGELINE1DELTA
)).GetValue();
195 sal_Int32 nVal2
= ((SdrEdgeLine2DeltaItem
&)rSet
.Get(SDRATTR_EDGELINE2DELTA
)).GetValue();
196 sal_Int32 nVal3
= ((SdrEdgeLine3DeltaItem
&)rSet
.Get(SDRATTR_EDGELINE3DELTA
)).GetValue();
198 if(eKind
== SDREDGE_ORTHOLINES
|| eKind
== SDREDGE_BEZIER
)
200 sal_Int32 nVals
[3] = { nVal1
, nVal2
, nVal3
};
203 if(aEdgeInfo
.nObj1Lines
>= 2 && n
< 3)
205 aEdgeInfo
.ImpSetLineVersatz(OBJ1LINE2
, *pEdgeTrack
, nVals
[n
]);
209 if(aEdgeInfo
.nObj1Lines
>= 3 && n
< 3)
211 aEdgeInfo
.ImpSetLineVersatz(OBJ1LINE3
, *pEdgeTrack
, nVals
[n
]);
215 if(aEdgeInfo
.nMiddleLine
!= 0xFFFF && n
< 3)
217 aEdgeInfo
.ImpSetLineVersatz(MIDDLELINE
, *pEdgeTrack
, nVals
[n
]);
221 if(aEdgeInfo
.nObj2Lines
>= 3 && n
< 3)
223 aEdgeInfo
.ImpSetLineVersatz(OBJ2LINE3
, *pEdgeTrack
, nVals
[n
]);
227 if(aEdgeInfo
.nObj2Lines
>= 2 && n
< 3)
229 aEdgeInfo
.ImpSetLineVersatz(OBJ2LINE2
, *pEdgeTrack
, nVals
[n
]);
233 else if(eKind
== SDREDGE_THREELINES
)
235 bool bHor1
= aEdgeInfo
.nAngle1
== 0 || aEdgeInfo
.nAngle1
== 18000;
236 bool bHor2
= aEdgeInfo
.nAngle2
== 0 || aEdgeInfo
.nAngle2
== 18000;
240 aEdgeInfo
.aObj1Line2
.X() = nVal1
;
244 aEdgeInfo
.aObj1Line2
.Y() = nVal1
;
249 aEdgeInfo
.aObj2Line2
.X() = nVal2
;
253 aEdgeInfo
.aObj2Line2
.Y() = nVal2
;
260 void SdrEdgeObj::ImpSetEdgeInfoToAttr()
262 const SfxItemSet
& rSet
= GetObjectItemSet();
263 SdrEdgeKind eKind
= ((SdrEdgeKindItem
&)(rSet
.Get(SDRATTR_EDGEKIND
))).GetValue();
264 sal_Int32 nValAnz
= ((SdrEdgeLineDeltaAnzItem
&)rSet
.Get(SDRATTR_EDGELINEDELTAANZ
)).GetValue();
265 sal_Int32 nVal1
= ((SdrEdgeLine1DeltaItem
&)rSet
.Get(SDRATTR_EDGELINE1DELTA
)).GetValue();
266 sal_Int32 nVal2
= ((SdrEdgeLine2DeltaItem
&)rSet
.Get(SDRATTR_EDGELINE2DELTA
)).GetValue();
267 sal_Int32 nVal3
= ((SdrEdgeLine3DeltaItem
&)rSet
.Get(SDRATTR_EDGELINE3DELTA
)).GetValue();
268 sal_Int32 nVals
[3] = { nVal1
, nVal2
, nVal3
};
271 if(eKind
== SDREDGE_ORTHOLINES
|| eKind
== SDREDGE_BEZIER
)
273 if(aEdgeInfo
.nObj1Lines
>= 2 && n
< 3)
275 nVals
[n
] = aEdgeInfo
.ImpGetLineVersatz(OBJ1LINE2
, *pEdgeTrack
);
279 if(aEdgeInfo
.nObj1Lines
>= 3 && n
< 3)
281 nVals
[n
] = aEdgeInfo
.ImpGetLineVersatz(OBJ1LINE3
, *pEdgeTrack
);
285 if(aEdgeInfo
.nMiddleLine
!= 0xFFFF && n
< 3)
287 nVals
[n
] = aEdgeInfo
.ImpGetLineVersatz(MIDDLELINE
, *pEdgeTrack
);
291 if(aEdgeInfo
.nObj2Lines
>= 3 && n
< 3)
293 nVals
[n
] = aEdgeInfo
.ImpGetLineVersatz(OBJ2LINE3
, *pEdgeTrack
);
297 if(aEdgeInfo
.nObj2Lines
>= 2 && n
< 3)
299 nVals
[n
] = aEdgeInfo
.ImpGetLineVersatz(OBJ2LINE2
, *pEdgeTrack
);
303 else if(eKind
== SDREDGE_THREELINES
)
305 bool bHor1
= aEdgeInfo
.nAngle1
== 0 || aEdgeInfo
.nAngle1
== 18000;
306 bool bHor2
= aEdgeInfo
.nAngle2
== 0 || aEdgeInfo
.nAngle2
== 18000;
309 nVals
[0] = bHor1
? aEdgeInfo
.aObj1Line2
.X() : aEdgeInfo
.aObj1Line2
.Y();
310 nVals
[1] = bHor2
? aEdgeInfo
.aObj2Line2
.X() : aEdgeInfo
.aObj2Line2
.Y();
313 if(n
!= nValAnz
|| nVals
[0] != nVal1
|| nVals
[1] != nVal2
|| nVals
[2] != nVal3
)
315 // Here no more notifying is necessary, just local changes are OK.
318 GetProperties().SetObjectItemDirect(SdrEdgeLineDeltaAnzItem(n
));
321 if(nVals
[0] != nVal1
)
323 GetProperties().SetObjectItemDirect(SdrEdgeLine1DeltaItem(nVals
[0]));
326 if(nVals
[1] != nVal2
)
328 GetProperties().SetObjectItemDirect(SdrEdgeLine2DeltaItem(nVals
[1]));
331 if(nVals
[2] != nVal3
)
333 GetProperties().SetObjectItemDirect(SdrEdgeLine3DeltaItem(nVals
[2]));
338 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE3DELTA
);
343 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE2DELTA
);
348 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE1DELTA
);
353 void SdrEdgeObj::TakeObjInfo(SdrObjTransformInfoRec
& rInfo
) const
355 // #i54102# allow rotation, mirror and shear
356 rInfo
.bRotateFreeAllowed
= true;
357 rInfo
.bRotate90Allowed
= true;
358 rInfo
.bMirrorFreeAllowed
= true;
359 rInfo
.bMirror45Allowed
= true;
360 rInfo
.bMirror90Allowed
= true;
361 rInfo
.bTransparenceAllowed
= sal_False
;
362 rInfo
.bGradientAllowed
= sal_False
;
363 rInfo
.bShearAllowed
= true;
364 rInfo
.bEdgeRadiusAllowed
= sal_False
;
365 bool bCanConv
=!HasText() || ImpCanConvTextToCurve();
366 rInfo
.bCanConvToPath
=bCanConv
;
367 rInfo
.bCanConvToPoly
=bCanConv
;
368 rInfo
.bCanConvToContour
= (rInfo
.bCanConvToPoly
|| LineGeometryUsageIsNecessary());
371 sal_uInt16
SdrEdgeObj::GetObjIdentifier() const
373 return sal_uInt16(OBJ_EDGE
);
376 const Rectangle
& SdrEdgeObj::GetCurrentBoundRect() const
380 ((SdrEdgeObj
*)this)->ImpRecalcEdgeTrack();
383 return SdrTextObj::GetCurrentBoundRect();
386 const Rectangle
& SdrEdgeObj::GetSnapRect() const
390 ((SdrEdgeObj
*)this)->ImpRecalcEdgeTrack();
393 return SdrTextObj::GetSnapRect();
396 void SdrEdgeObj::RecalcSnapRect()
398 maSnapRect
=pEdgeTrack
->GetBoundRect();
401 void SdrEdgeObj::TakeUnrotatedSnapRect(Rectangle
& rRect
) const
406 bool SdrEdgeObj::IsNode() const
411 SdrGluePoint
SdrEdgeObj::GetVertexGluePoint(sal_uInt16 nNum
) const
414 sal_uInt16 nPntAnz
=pEdgeTrack
->GetPointCount();
417 Point aOfs
= GetSnapRect().Center();
418 if (nNum
==2 && GetConnectedNode(sal_True
)==NULL
) aPt
=(*pEdgeTrack
)[0];
419 else if (nNum
==3 && GetConnectedNode(sal_False
)==NULL
) aPt
=(*pEdgeTrack
)[nPntAnz
-1];
421 if ((nPntAnz
& 1) ==1) {
422 aPt
=(*pEdgeTrack
)[nPntAnz
/2];
424 Point
aPt1((*pEdgeTrack
)[nPntAnz
/2-1]);
425 Point
aPt2((*pEdgeTrack
)[nPntAnz
/2]);
434 SdrGluePoint
aGP(aPt
);
435 aGP
.SetPercent(sal_False
);
439 SdrGluePoint
SdrEdgeObj::GetCornerGluePoint(sal_uInt16 nNum
) const
441 return GetVertexGluePoint(nNum
);
444 const SdrGluePointList
* SdrEdgeObj::GetGluePointList() const
446 return NULL
; // no user defined glue points for connectors
449 SdrGluePointList
* SdrEdgeObj::ForceGluePointList()
451 return NULL
; // no user defined glue points for connectors
454 bool SdrEdgeObj::IsEdge() const
459 void SdrEdgeObj::ConnectToNode(bool bTail1
, SdrObject
* pObj
)
461 SdrObjConnection
& rCon
=GetConnection(bTail1
);
462 DisconnectFromNode(bTail1
);
464 pObj
->AddListener(*this);
467 // #i120437# If connection is set, reset bEdgeTrackUserDefined
468 bEdgeTrackUserDefined
= false;
474 void SdrEdgeObj::DisconnectFromNode(bool bTail1
)
476 SdrObjConnection
& rCon
=GetConnection(bTail1
);
477 if (rCon
.pObj
!=NULL
) {
478 rCon
.pObj
->RemoveListener(*this);
483 SdrObject
* SdrEdgeObj::GetConnectedNode(bool bTail1
) const
485 SdrObject
* pObj
=GetConnection(bTail1
).pObj
;
486 if (pObj
!=NULL
&& (pObj
->GetPage()!=pPage
|| !pObj
->IsInserted())) pObj
=NULL
;
490 bool SdrEdgeObj::CheckNodeConnection(bool bTail1
) const
493 const SdrObjConnection
& rCon
=GetConnection(bTail1
);
494 sal_uInt16 nPtAnz
=pEdgeTrack
->GetPointCount();
495 if (rCon
.pObj
!=NULL
&& rCon
.pObj
->GetPage()==pPage
&& nPtAnz
!=0) {
496 const SdrGluePointList
* pGPL
=rCon
.pObj
->GetGluePointList();
497 sal_uInt16 nConAnz
=pGPL
==NULL
? 0 : pGPL
->GetCount();
498 sal_uInt16 nGesAnz
=nConAnz
+8;
499 Point
aTail(bTail1
? (*pEdgeTrack
)[0] : (*pEdgeTrack
)[sal_uInt16(nPtAnz
-1)]);
500 for (sal_uInt16 i
=0; i
<nGesAnz
&& !bRet
; i
++) {
501 if (i
<nConAnz
) { // UserDefined
502 bRet
=aTail
==(*pGPL
)[i
].GetAbsolutePos(*rCon
.pObj
);
503 } else if (i
<nConAnz
+4) { // Vertex
504 SdrGluePoint
aPt(rCon
.pObj
->GetVertexGluePoint(i
-nConAnz
));
505 bRet
=aTail
==aPt
.GetAbsolutePos(*rCon
.pObj
);
507 SdrGluePoint
aPt(rCon
.pObj
->GetCornerGluePoint(i
-nConAnz
-4));
508 bRet
=aTail
==aPt
.GetAbsolutePos(*rCon
.pObj
);
515 void SdrEdgeObj::ImpSetTailPoint(bool bTail1
, const Point
& rPt
)
517 sal_uInt16 nPtAnz
=pEdgeTrack
->GetPointCount();
519 (*pEdgeTrack
)[0]=rPt
;
520 (*pEdgeTrack
)[1]=rPt
;
521 } else if (nPtAnz
==1) {
522 if (!bTail1
) (*pEdgeTrack
)[1]=rPt
;
523 else { (*pEdgeTrack
)[1]=(*pEdgeTrack
)[0]; (*pEdgeTrack
)[0]=rPt
; }
525 if (!bTail1
) (*pEdgeTrack
)[sal_uInt16(nPtAnz
-1)]=rPt
;
526 else (*pEdgeTrack
)[0]=rPt
;
528 ImpRecalcEdgeTrack();
532 void SdrEdgeObj::ImpDirtyEdgeTrack()
534 if ( !bEdgeTrackUserDefined
|| !(GetModel() && GetModel()->isLocked()) )
535 bEdgeTrackDirty
= sal_True
;
538 void SdrEdgeObj::ImpUndirtyEdgeTrack()
540 if (bEdgeTrackDirty
&& (GetModel() && GetModel()->isLocked()) ) {
541 ImpRecalcEdgeTrack();
545 void SdrEdgeObj::ImpRecalcEdgeTrack()
547 // #i120437# if bEdgeTrackUserDefined, do not recalculate
548 if(bEdgeTrackUserDefined
)
553 // #i120437# also not when model locked during import, but remember
554 if(!GetModel() || GetModel()->isLocked())
561 if(IsBoundRectCalculationRunning())
563 // This object is involved into another ImpRecalcEdgeTrack() call
564 // from another SdrEdgeObj. Do not calculate again to avoid loop.
565 // Also, do not change bEdgeTrackDirty so that it gets recalculated
566 // later at the first non-looping call.
572 // #i123048# If layouting was ever suppressed, it needs to be done once
573 // and the attr need to be set at EdgeInfo, else these attr *will be lost*
574 // in the following call to ImpSetEdgeInfoToAttr() sice they were never
576 *pEdgeTrack
=ImpCalcEdgeTrack(*pEdgeTrack
,aCon1
,aCon2
,&aEdgeInfo
);
577 ImpSetAttrToEdgeInfo();
578 mbSuppressed
= false;
581 // To not run in a depth loop, use a coloring algorythm on
582 // SdrEdgeObj BoundRect calculations
583 mbBoundRectCalculationRunning
= sal_True
;
585 Rectangle aBoundRect0
; if (pUserCall
!=NULL
) aBoundRect0
=GetCurrentBoundRect();
587 *pEdgeTrack
=ImpCalcEdgeTrack(*pEdgeTrack
,aCon1
,aCon2
,&aEdgeInfo
);
588 ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
589 bEdgeTrackDirty
=sal_False
;
591 // Only redraw here, no object change
594 SendUserCall(SDRUSERCALL_RESIZE
,aBoundRect0
);
596 mbBoundRectCalculationRunning
= sal_False
;
600 sal_uInt16
SdrEdgeObj::ImpCalcEscAngle(SdrObject
* pObj
, const Point
& rPt
) const
602 if (pObj
==NULL
) return SDRESC_ALL
;
603 Rectangle
aR(pObj
->GetSnapRect());
604 long dxl
=rPt
.X()-aR
.Left();
605 long dyo
=rPt
.Y()-aR
.Top();
606 long dxr
=aR
.Right()-rPt
.X();
607 long dyu
=aR
.Bottom()-rPt
.Y();
608 bool bxMitt
=std::abs(dxl
-dxr
)<2;
609 bool byMitt
=std::abs(dyo
-dyu
)<2;
610 long dx
=std::min(dxl
,dxr
);
611 long dy
=std::min(dyo
,dyu
);
612 bool bDiag
=std::abs(dx
-dy
)<2;
613 if (bxMitt
&& byMitt
) return SDRESC_ALL
; // in the center
614 if (bDiag
) { // diagonally
616 if (byMitt
) nRet
|=SDRESC_VERT
;
617 if (bxMitt
) nRet
|=SDRESC_HORZ
;
618 if (dxl
<dxr
) { // left
619 if (dyo
<dyu
) nRet
|=SDRESC_LEFT
| SDRESC_TOP
;
620 else nRet
|=SDRESC_LEFT
| SDRESC_BOTTOM
;
622 if (dyo
<dyu
) nRet
|=SDRESC_RIGHT
| SDRESC_TOP
;
623 else nRet
|=SDRESC_RIGHT
| SDRESC_BOTTOM
;
627 if (dx
<dy
) { // horizontal
628 if (bxMitt
) return SDRESC_HORZ
;
629 if (dxl
<dxr
) return SDRESC_LEFT
;
630 else return SDRESC_RIGHT
;
632 if (byMitt
) return SDRESC_VERT
;
633 if (dyo
<dyu
) return SDRESC_TOP
;
634 else return SDRESC_BOTTOM
;
638 XPolygon
SdrEdgeObj::ImpCalcObjToCenter(const Point
& rStPt
, long nEscAngle
, const Rectangle
& rRect
, const Point
& rMeeting
) const
641 aXP
.Insert(XPOLY_APPEND
,rStPt
,XPOLY_NORMAL
);
642 bool bRts
=nEscAngle
==0;
643 bool bObn
=nEscAngle
==9000;
644 bool bLks
=nEscAngle
==18000;
645 bool bUnt
=nEscAngle
==27000;
647 Point
aP1(rStPt
); // mandatory difference first,...
648 if (bLks
) aP1
.X()=rRect
.Left();
649 if (bRts
) aP1
.X()=rRect
.Right();
650 if (bObn
) aP1
.Y()=rRect
.Top();
651 if (bUnt
) aP1
.Y()=rRect
.Bottom();
653 Point
aP2(aP1
); // ...now increase to Meeting height, if necessary
654 if (bLks
&& rMeeting
.X()<=aP2
.X()) aP2
.X()=rMeeting
.X();
655 if (bRts
&& rMeeting
.X()>=aP2
.X()) aP2
.X()=rMeeting
.X();
656 if (bObn
&& rMeeting
.Y()<=aP2
.Y()) aP2
.Y()=rMeeting
.Y();
657 if (bUnt
&& rMeeting
.Y()>=aP2
.Y()) aP2
.Y()=rMeeting
.Y();
658 aXP
.Insert(XPOLY_APPEND
,aP2
,XPOLY_NORMAL
);
661 if ((bLks
&& rMeeting
.X()>aP2
.X()) || (bRts
&& rMeeting
.X()<aP2
.X())) { // around
662 if (rMeeting
.Y()<aP2
.Y()) {
664 if (rMeeting
.Y()<aP3
.Y()) aP3
.Y()=rMeeting
.Y();
666 aP3
.Y()=rRect
.Bottom();
667 if (rMeeting
.Y()>aP3
.Y()) aP3
.Y()=rMeeting
.Y();
669 aXP
.Insert(XPOLY_APPEND
,aP3
,XPOLY_NORMAL
);
670 if (aP3
.Y()!=rMeeting
.Y()) {
671 aP3
.X()=rMeeting
.X();
672 aXP
.Insert(XPOLY_APPEND
,aP3
,XPOLY_NORMAL
);
675 if ((bObn
&& rMeeting
.Y()>aP2
.Y()) || (bUnt
&& rMeeting
.Y()<aP2
.Y())) { // around
676 if (rMeeting
.X()<aP2
.X()) {
677 aP3
.X()=rRect
.Left();
678 if (rMeeting
.X()<aP3
.X()) aP3
.X()=rMeeting
.X();
680 aP3
.X()=rRect
.Right();
681 if (rMeeting
.X()>aP3
.X()) aP3
.X()=rMeeting
.X();
683 aXP
.Insert(XPOLY_APPEND
,aP3
,XPOLY_NORMAL
);
684 if (aP3
.X()!=rMeeting
.X()) {
685 aP3
.Y()=rMeeting
.Y();
686 aXP
.Insert(XPOLY_APPEND
,aP3
,XPOLY_NORMAL
);
690 if (aXP
.GetPointCount()>4) {
691 OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
697 XPolygon
SdrEdgeObj::ImpCalcEdgeTrack(const XPolygon
& rTrack0
, SdrObjConnection
& rCon1
, SdrObjConnection
& rCon2
, SdrEdgeInfoRec
* pInfo
) const
700 SdrGluePoint aGP1
,aGP2
;
701 sal_uInt16 nEsc1
=SDRESC_ALL
,nEsc2
=SDRESC_ALL
;
702 Rectangle aBoundRect1
;
703 Rectangle aBoundRect2
;
704 Rectangle aBewareRect1
;
705 Rectangle aBewareRect2
;
706 // first, get the old corner points
707 if (rTrack0
.GetPointCount()!=0) {
709 sal_uInt16 nSiz
=rTrack0
.GetPointCount();
713 if (!aOutRect
.IsEmpty()) {
714 aPt1
=aOutRect
.TopLeft();
715 aPt2
=aOutRect
.BottomRight();
719 // #i54102# To allow interactive preview, do also if not inserted
720 bool bCon1
=rCon1
.pObj
!=NULL
&& rCon1
.pObj
->GetPage()==pPage
;
721 bool bCon2
=rCon2
.pObj
!=NULL
&& rCon2
.pObj
->GetPage()==pPage
;
723 const SfxItemSet
& rSet
= GetObjectItemSet();
726 if (rCon1
.pObj
==(SdrObject
*)this)
728 // check, just in case
729 aBoundRect1
=aOutRect
;
733 aBoundRect1
= rCon1
.pObj
->GetCurrentBoundRect();
735 aBoundRect1
.Move(rCon1
.aObjOfs
.X(),rCon1
.aObjOfs
.Y());
736 aBewareRect1
=aBoundRect1
;
738 sal_Int32 nH
= ((SdrEdgeNode1HorzDistItem
&)rSet
.Get(SDRATTR_EDGENODE1HORZDIST
)).GetValue();
739 sal_Int32 nV
= ((SdrEdgeNode1VertDistItem
&)rSet
.Get(SDRATTR_EDGENODE1VERTDIST
)).GetValue();
741 aBewareRect1
.Left()-=nH
;
742 aBewareRect1
.Right()+=nH
;
743 aBewareRect1
.Top()-=nV
;
744 aBewareRect1
.Bottom()+=nV
;
746 aBoundRect1
=Rectangle(aPt1
,aPt1
);
747 aBoundRect1
.Move(rCon1
.aObjOfs
.X(),rCon1
.aObjOfs
.Y());
748 aBewareRect1
=aBoundRect1
;
751 if (rCon2
.pObj
==(SdrObject
*)this) { // check, just in case
752 aBoundRect2
=aOutRect
;
756 aBoundRect2
= rCon2
.pObj
->GetCurrentBoundRect();
758 aBoundRect2
.Move(rCon2
.aObjOfs
.X(),rCon2
.aObjOfs
.Y());
759 aBewareRect2
=aBoundRect2
;
761 sal_Int32 nH
= ((SdrEdgeNode2HorzDistItem
&)rSet
.Get(SDRATTR_EDGENODE2HORZDIST
)).GetValue();
762 sal_Int32 nV
= ((SdrEdgeNode2VertDistItem
&)rSet
.Get(SDRATTR_EDGENODE2VERTDIST
)).GetValue();
764 aBewareRect2
.Left()-=nH
;
765 aBewareRect2
.Right()+=nH
;
766 aBewareRect2
.Top()-=nV
;
767 aBewareRect2
.Bottom()+=nV
;
769 aBoundRect2
=Rectangle(aPt2
,aPt2
);
770 aBoundRect2
.Move(rCon2
.aObjOfs
.X(),rCon2
.aObjOfs
.Y());
771 aBewareRect2
=aBoundRect2
;
774 sal_uIntPtr nBestQual
=0xFFFFFFFF;
775 SdrEdgeInfoRec aBestInfo
;
776 bool bAuto1
=bCon1
&& rCon1
.bBestVertex
;
777 bool bAuto2
=bCon2
&& rCon2
.bBestVertex
;
778 if (bAuto1
) rCon1
.bAutoVertex
=sal_True
;
779 if (bAuto2
) rCon2
.bAutoVertex
=sal_True
;
780 sal_uInt16 nBestAuto1
=0;
781 sal_uInt16 nBestAuto2
=0;
782 sal_uInt16 nAnz1
=bAuto1
? 4 : 1;
783 sal_uInt16 nAnz2
=bAuto2
? 4 : 1;
784 for (sal_uInt16 nNum1
=0; nNum1
<nAnz1
; nNum1
++) {
785 if (bAuto1
) rCon1
.nConId
=nNum1
;
786 if (bCon1
&& rCon1
.TakeGluePoint(aGP1
,sal_True
)) {
788 nEsc1
=aGP1
.GetEscDir();
789 if (nEsc1
==SDRESC_SMART
) nEsc1
=ImpCalcEscAngle(rCon1
.pObj
,aPt1
-rCon1
.aObjOfs
);
791 for (sal_uInt16 nNum2
=0; nNum2
<nAnz2
; nNum2
++) {
792 if (bAuto2
) rCon2
.nConId
=nNum2
;
793 if (bCon2
&& rCon2
.TakeGluePoint(aGP2
,sal_True
)) {
795 nEsc2
=aGP2
.GetEscDir();
796 if (nEsc2
==SDRESC_SMART
) nEsc2
=ImpCalcEscAngle(rCon2
.pObj
,aPt2
-rCon2
.aObjOfs
);
798 for (long nA1
=0; nA1
<36000; nA1
+=9000) {
799 sal_uInt16 nE1
=nA1
==0 ? SDRESC_RIGHT
: nA1
==9000 ? SDRESC_TOP
: nA1
==18000 ? SDRESC_LEFT
: nA1
==27000 ? SDRESC_BOTTOM
: 0;
800 for (long nA2
=0; nA2
<36000; nA2
+=9000) {
801 sal_uInt16 nE2
=nA2
==0 ? SDRESC_RIGHT
: nA2
==9000 ? SDRESC_TOP
: nA2
==18000 ? SDRESC_LEFT
: nA2
==27000 ? SDRESC_BOTTOM
: 0;
802 if ((nEsc1
&nE1
)!=0 && (nEsc2
&nE2
)!=0) {
804 SdrEdgeInfoRec aInfo
;
805 if (pInfo
!=NULL
) aInfo
=*pInfo
;
806 XPolygon
aXP(ImpCalcEdgeTrack(aPt1
,nA1
,aBoundRect1
,aBewareRect1
,aPt2
,nA2
,aBoundRect2
,aBewareRect2
,&nQual
,&aInfo
));
807 if (nQual
<nBestQual
) {
819 if (bAuto1
) rCon1
.nConId
=nBestAuto1
;
820 if (bAuto2
) rCon2
.nConId
=nBestAuto2
;
821 if (pInfo
!=NULL
) *pInfo
=aBestInfo
;
825 XPolygon
SdrEdgeObj::ImpCalcEdgeTrack(const Point
& rPt1
, long nAngle1
, const Rectangle
& rBoundRect1
, const Rectangle
& rBewareRect1
,
826 const Point
& rPt2
, long nAngle2
, const Rectangle
& rBoundRect2
, const Rectangle
& rBewareRect2
,
827 sal_uIntPtr
* pnQuality
, SdrEdgeInfoRec
* pInfo
) const
829 SdrEdgeKind eKind
=((SdrEdgeKindItem
&)(GetObjectItem(SDRATTR_EDGEKIND
))).GetValue();
830 bool bRts1
=nAngle1
==0;
831 bool bObn1
=nAngle1
==9000;
832 bool bLks1
=nAngle1
==18000;
833 bool bUnt1
=nAngle1
==27000;
834 bool bHor1
=bLks1
|| bRts1
;
835 bool bVer1
=bObn1
|| bUnt1
;
836 bool bRts2
=nAngle2
==0;
837 bool bObn2
=nAngle2
==9000;
838 bool bLks2
=nAngle2
==18000;
839 bool bUnt2
=nAngle2
==27000;
840 bool bHor2
=bLks2
|| bRts2
;
841 bool bVer2
=bObn2
|| bUnt2
;
842 bool bInfo
=pInfo
!=NULL
;
845 pInfo
->nAngle1
=nAngle1
;
846 pInfo
->nAngle2
=nAngle2
;
849 pInfo
->nMiddleLine
=0xFFFF;
853 Rectangle
aBoundRect1 (rBoundRect1
);
854 Rectangle
aBoundRect2 (rBoundRect2
);
855 Rectangle
aBewareRect1(rBewareRect1
);
856 Rectangle
aBewareRect2(rBewareRect2
);
857 Point
aMeeting((aPt1
.X()+aPt2
.X()+1)/2,(aPt1
.Y()+aPt2
.Y()+1)/2);
858 if (eKind
==SDREDGE_ONELINE
) {
862 if (pnQuality
!=NULL
) {
863 *pnQuality
=std::abs(rPt1
.X()-rPt2
.X())+std::abs(rPt1
.Y()-rPt2
.Y());
866 } else if (eKind
==SDREDGE_THREELINES
) {
872 if (bRts1
) aXP
[1].X()=aBewareRect1
.Right(); //+=500;
873 if (bObn1
) aXP
[1].Y()=aBewareRect1
.Top(); //-=500;
874 if (bLks1
) aXP
[1].X()=aBewareRect1
.Left(); //-=500;
875 if (bUnt1
) aXP
[1].Y()=aBewareRect1
.Bottom(); //+=500;
876 if (bRts2
) aXP
[2].X()=aBewareRect2
.Right(); //+=500;
877 if (bObn2
) aXP
[2].Y()=aBewareRect2
.Top(); //-=500;
878 if (bLks2
) aXP
[2].X()=aBewareRect2
.Left(); //-=500;
879 if (bUnt2
) aXP
[2].Y()=aBewareRect2
.Bottom(); //+=500;
880 if (pnQuality
!=NULL
) {
881 long nQ
=std::abs(aXP
[1].X()-aXP
[0].X())+std::abs(aXP
[1].Y()-aXP
[0].Y());
882 nQ
+=std::abs(aXP
[2].X()-aXP
[1].X())+std::abs(aXP
[2].Y()-aXP
[1].Y());
883 nQ
+=std::abs(aXP
[3].X()-aXP
[2].X())+std::abs(aXP
[3].Y()-aXP
[2].Y());
890 aXP
[1].X()+=pInfo
->aObj1Line2
.X();
892 aXP
[1].Y()+=pInfo
->aObj1Line2
.Y();
895 aXP
[2].X()+=pInfo
->aObj2Line2
.X();
897 aXP
[2].Y()+=pInfo
->aObj2Line2
.Y();
902 sal_uInt16 nIntersections
=0;
904 Point
aC1(aBewareRect1
.Center());
905 Point
aC2(aBewareRect2
.Center());
906 if (aBewareRect1
.Left()<=aBewareRect2
.Right() && aBewareRect1
.Right()>=aBewareRect2
.Left()) {
907 // overlapping on the x axis
908 long n1
=std::max(aBewareRect1
.Left(),aBewareRect2
.Left());
909 long n2
=std::min(aBewareRect1
.Right(),aBewareRect2
.Right());
910 aMeeting
.X()=(n1
+n2
+1)/2;
912 // otherwise the center point of the empty space
913 if (aC1
.X()<aC2
.X()) {
914 aMeeting
.X()=(aBewareRect1
.Right()+aBewareRect2
.Left()+1)/2;
916 aMeeting
.X()=(aBewareRect1
.Left()+aBewareRect2
.Right()+1)/2;
919 if (aBewareRect1
.Top()<=aBewareRect2
.Bottom() && aBewareRect1
.Bottom()>=aBewareRect2
.Top()) {
920 // overlapping on the x axis
921 long n1
=std::max(aBewareRect1
.Top(),aBewareRect2
.Top());
922 long n2
=std::min(aBewareRect1
.Bottom(),aBewareRect2
.Bottom());
923 aMeeting
.Y()=(n1
+n2
+1)/2;
925 // otherwise the center point of the empty space
926 if (aC1
.Y()<aC2
.Y()) {
927 aMeeting
.Y()=(aBewareRect1
.Bottom()+aBewareRect2
.Top()+1)/2;
929 aMeeting
.Y()=(aBewareRect1
.Top()+aBewareRect2
.Bottom()+1)/2;
932 // Here, there are three cases:
933 // 1. both go into the same direction
934 // 2. both go into opposite directions
935 // 3. one is vertical, the other is horizontal
936 long nXMin
=std::min(aBewareRect1
.Left(),aBewareRect2
.Left());
937 long nXMax
=std::max(aBewareRect1
.Right(),aBewareRect2
.Right());
938 long nYMin
=std::min(aBewareRect1
.Top(),aBewareRect2
.Top());
939 long nYMax
=std::max(aBewareRect1
.Bottom(),aBewareRect2
.Bottom());
940 bool bBewareOverlap
=aBewareRect1
.Right()>aBewareRect2
.Left() && aBewareRect1
.Left()<aBewareRect2
.Right() &&
941 aBewareRect1
.Bottom()>aBewareRect2
.Top() && aBewareRect1
.Top()<aBewareRect2
.Bottom();
942 unsigned nMainCase
=3;
943 if (nAngle1
==nAngle2
) nMainCase
=1;
944 else if ((bHor1
&& bHor2
) || (bVer1
&& bVer2
)) nMainCase
=2;
945 if (nMainCase
==1) { // case 1 (both go in one direction) is possible
946 if (bVer1
) aMeeting
.X()=(aPt1
.X()+aPt2
.X()+1)/2; // Here, this is better than
947 if (bHor1
) aMeeting
.Y()=(aPt1
.Y()+aPt2
.Y()+1)/2; // using center point of empty space
948 // bX1Ok means that the vertical exiting Obj1 doesn't conflict with Obj2, ...
949 bool bX1Ok
=aPt1
.X()<=aBewareRect2
.Left() || aPt1
.X()>=aBewareRect2
.Right();
950 bool bX2Ok
=aPt2
.X()<=aBewareRect1
.Left() || aPt2
.X()>=aBewareRect1
.Right();
951 bool bY1Ok
=aPt1
.Y()<=aBewareRect2
.Top() || aPt1
.Y()>=aBewareRect2
.Bottom();
952 bool bY2Ok
=aPt2
.Y()<=aBewareRect1
.Top() || aPt2
.Y()>=aBewareRect1
.Bottom();
953 if (bLks1
&& (bY1Ok
|| aBewareRect1
.Left()<aBewareRect2
.Right()) && (bY2Ok
|| aBewareRect2
.Left()<aBewareRect1
.Right())) {
956 if (bRts1
&& (bY1Ok
|| aBewareRect1
.Right()>aBewareRect2
.Left()) && (bY2Ok
|| aBewareRect2
.Right()>aBewareRect1
.Left())) {
959 if (bObn1
&& (bX1Ok
|| aBewareRect1
.Top()<aBewareRect2
.Bottom()) && (bX2Ok
|| aBewareRect2
.Top()<aBewareRect1
.Bottom())) {
962 if (bUnt1
&& (bX1Ok
|| aBewareRect1
.Bottom()>aBewareRect2
.Top()) && (bX2Ok
|| aBewareRect2
.Bottom()>aBewareRect1
.Top())) {
965 } else if (nMainCase
==2) {
967 if (bHor1
) { // both horizontal
969 (legend: line exits to the left (-|), right (|-))
971 2.1: Facing; overlap only on y axis
976 2.2, 2.3: Facing, offset vertically; no overlap on either
982 2.4, 2.5: One below the other; overlap only on y axis
987 2.6, 2.7: Not facing, offset vertically; no overlap on either
993 2.8: Not facing; overlap only on y axis
998 2.9: The objects's BewareRects overlap on x and y axis
1000 These cases, with some modifications are also valid for
1001 horizontal line exits.
1002 Cases 2.1 through 2.7 are covered well enough with the
1003 default meetings. Only for cases 2.8 and 2.9 do we determine
1004 special meeting points here.
1007 // normalization; be aR1 the one exiting to the right,
1008 // be aR2 the one exiting to the left
1009 Rectangle
aBewR1(bRts1
? aBewareRect1
: aBewareRect2
);
1010 Rectangle
aBewR2(bRts1
? aBewareRect2
: aBewareRect1
);
1011 Rectangle
aBndR1(bRts1
? aBoundRect1
: aBoundRect2
);
1012 Rectangle
aBndR2(bRts1
? aBoundRect2
: aBoundRect1
);
1013 if (aBewR1
.Bottom()>aBewR2
.Top() && aBewR1
.Top()<aBewR2
.Bottom()) {
1014 // overlap on y axis; cases 2.1, 2.8, 2.9
1015 if (aBewR1
.Right()>aBewR2
.Left()) {
1017 Case 2.8: always going around on the outside
1018 (bDirect=sal_False).
1020 Case 2.9 could also be a direct connection (in the
1021 case that the BewareRects overlap only slightly and
1022 the BoundRects don't overlap at all and if the
1023 line exits would otherwise violate the respective
1024 other object's BewareRect).
1026 bool bCase29Direct
= false;
1027 bool bCase29
=aBewR1
.Right()>aBewR2
.Left();
1028 if (aBndR1
.Right()<=aBndR2
.Left()) { // case 2.9 without BoundRect overlap
1029 if ((aPt1
.Y()>aBewareRect2
.Top() && aPt1
.Y()<aBewareRect2
.Bottom()) ||
1030 (aPt2
.Y()>aBewareRect1
.Top() && aPt2
.Y()<aBewareRect1
.Bottom())) {
1031 bCase29Direct
= true;
1034 if (!bCase29Direct
) {
1035 bool bObenLang
=std::abs(nYMin
-aMeeting
.Y())<=std::abs(nYMax
-aMeeting
.Y());
1042 // now make sure that the surrounded object
1044 if ((aBewR1
.Center().Y()<aBewR2
.Center().Y()) != bObenLang
) {
1045 aMeeting
.X()=aBewR2
.Right();
1047 aMeeting
.X()=aBewR1
.Left();
1051 // We need a direct connection (3-line Z connection),
1052 // because we have to violate the BewareRects.
1053 // Use rule of three to scale down the BewareRects.
1054 long nWant1
=aBewR1
.Right()-aBndR1
.Right(); // distance at Obj1
1055 long nWant2
=aBndR2
.Left()-aBewR2
.Left(); // distance at Obj2
1056 long nSpace
=aBndR2
.Left()-aBndR1
.Right(); // available space
1057 long nGet1
=BigMulDiv(nWant1
,nSpace
,nWant1
+nWant2
);
1058 long nGet2
=nSpace
-nGet1
;
1059 if (bRts1
) { // revert normalization
1060 aBewareRect1
.Right()+=nGet1
-nWant1
;
1061 aBewareRect2
.Left()-=nGet2
-nWant2
;
1063 aBewareRect2
.Right()+=nGet1
-nWant1
;
1064 aBewareRect1
.Left()-=nGet2
-nWant2
;
1066 nIntersections
++; // lower quality
1070 } else if (bVer1
) { // both horizontal
1071 Rectangle
aBewR1(bUnt1
? aBewareRect1
: aBewareRect2
);
1072 Rectangle
aBewR2(bUnt1
? aBewareRect2
: aBewareRect1
);
1073 Rectangle
aBndR1(bUnt1
? aBoundRect1
: aBoundRect2
);
1074 Rectangle
aBndR2(bUnt1
? aBoundRect2
: aBoundRect1
);
1075 if (aBewR1
.Right()>aBewR2
.Left() && aBewR1
.Left()<aBewR2
.Right()) {
1076 // overlap on y axis; cases 2.1, 2.8, 2.9
1077 if (aBewR1
.Bottom()>aBewR2
.Top()) {
1079 Case 2.8 always going around on the outside (bDirect=sal_False).
1081 Case 2.9 could also be a direct connection (in the
1082 case that the BewareRects overlap only slightly and
1083 the BoundRects don't overlap at all and if the
1084 line exits would otherwise violate the respective
1085 other object's BewareRect).
1087 bool bCase29Direct
= false;
1088 bool bCase29
=aBewR1
.Bottom()>aBewR2
.Top();
1089 if (aBndR1
.Bottom()<=aBndR2
.Top()) { // case 2.9 without BoundRect overlap
1090 if ((aPt1
.X()>aBewareRect2
.Left() && aPt1
.X()<aBewareRect2
.Right()) ||
1091 (aPt2
.X()>aBewareRect1
.Left() && aPt2
.X()<aBewareRect1
.Right())) {
1092 bCase29Direct
= true;
1095 if (!bCase29Direct
) {
1096 bool bLinksLang
=std::abs(nXMin
-aMeeting
.X())<=std::abs(nXMax
-aMeeting
.X());
1103 // now make sure that the surrounded object
1105 if ((aBewR1
.Center().X()<aBewR2
.Center().X()) != bLinksLang
) {
1106 aMeeting
.Y()=aBewR2
.Bottom();
1108 aMeeting
.Y()=aBewR1
.Top();
1112 // We need a direct connection (3-line Z connection),
1113 // because we have to violate the BewareRects.
1114 // Use rule of three to scale down the BewareRects.
1115 long nWant1
=aBewR1
.Bottom()-aBndR1
.Bottom(); // difference at Obj1
1116 long nWant2
=aBndR2
.Top()-aBewR2
.Top(); // difference at Obj2
1117 long nSpace
=aBndR2
.Top()-aBndR1
.Bottom(); // available space
1118 long nGet1
=BigMulDiv(nWant1
,nSpace
,nWant1
+nWant2
);
1119 long nGet2
=nSpace
-nGet1
;
1120 if (bUnt1
) { // revert normalization
1121 aBewareRect1
.Bottom()+=nGet1
-nWant1
;
1122 aBewareRect2
.Top()-=nGet2
-nWant2
;
1124 aBewareRect2
.Bottom()+=nGet1
-nWant1
;
1125 aBewareRect1
.Top()-=nGet2
-nWant2
;
1127 nIntersections
++; // lower quality
1132 } else if (nMainCase
==3) { // case 3: one horizontal, the other vertical
1134 The line exits to the:
1143 * . * . * -- no overlap, at most might touch
1144 . . . . . -- overlap
1145 * . |- . * -- same height
1146 . . . . . -- overlap
1147 * . * . * -- no overlap, at most might touch
1149 Overall, there are 96 possible constellations, some of these can't even
1150 be unambiguously assigned to a certain case/method of handling.
1153 3.1: All those constellations that are covered reasonably well
1154 by the default MeetingPoint (20+12).
1156 T T T . _|_ _|_ . T T T these 12 * . * T * * . * . * * T * . * * . * . *
1157 . . . . _|_ _|_ . . . . constellations . . . . . . . . . T . . . . . T . . . .
1158 * . |- . * * . -| . * are covered * . |- . _|_ * . |- . T _|_ . -| . * T . -| . *
1159 . . . . T T . . . . only in . . . . _|_ . . . . . _|_ . . . . . . . . .
1160 _|__|__|_ . T T . _|__|__|_ part: * . * _|_ * * . * . * * _|_ * . * * . * . *
1162 The last 16 of these cases can be excluded, if the objects face each other openly.
1165 3.2: The objects face each other openly, thus a connection using only two lines is possible (4+20);
1166 This case is priority #1.
1167 * . * . T T . * . * these 20 * . * T * * T * . * * . * . * * . * . *
1168 . . . . . . . . . . constellations . . . T T T T . . . . . . . . . . . . .
1169 * . |- . * * . -| . * are covered * . |-_|__|_ _|__|_-| . * * . |- T T T T -| . *
1170 . . . . . . . . . . only in . . . _|__|_ _|__|_ . . . . . . . . . . . . .
1171 * . * . _|_ _|_ . * . * part: * . * _|_ * * _|_ * . * * . * . * * . * . *
1173 3.3: The line exits point away from the other object or or miss its back (52+4).
1174 _|__|__|__|_ * * _|__|__|__|_ * . . . * * . * . * these 4 * . * . * * . * . *
1175 _|__|__|__|_ . . _|__|__|__|_ T T T . . . . T T T constellations . . . T . . T . . .
1176 _|__|_ |- . * * . -| _|__|_ T T |- . * * . -| T T are covered * . |- . * * . -| . *
1177 _|__|__|_ . . . . _|__|__|_ T T T T . . T T T T only in . . . _|_ . . _|_ . . .
1178 * . * . * * . * . * T T T T * * T T T T part: * . * . * * . * . *
1182 Rectangle
aTmpR1(aBewareRect1
);
1183 Rectangle
aTmpR2(aBewareRect2
);
1184 if (bBewareOverlap
) {
1185 // overlapping BewareRects: use BoundRects for checking for case 3.2
1189 if ((((bRts1
&& aTmpR1
.Right ()<=aPt2
.X()) || (bLks1
&& aTmpR1
.Left()>=aPt2
.X())) &&
1190 ((bUnt2
&& aTmpR2
.Bottom()<=aPt1
.Y()) || (bObn2
&& aTmpR2
.Top ()>=aPt1
.Y()))) ||
1191 (((bRts2
&& aTmpR2
.Right ()<=aPt1
.X()) || (bLks2
&& aTmpR2
.Left()>=aPt1
.X())) &&
1192 ((bUnt1
&& aTmpR1
.Bottom()<=aPt2
.Y()) || (bObn1
&& aTmpR1
.Top ()>=aPt2
.Y())))) {
1193 // case 3.2 applies: connector with only 2 lines
1195 aMeeting
.X()=aPt2
.X();
1196 aMeeting
.Y()=aPt1
.Y();
1198 aMeeting
.X()=aPt1
.X();
1199 aMeeting
.Y()=aPt2
.Y();
1201 // in the case of overlapping BewareRects:
1202 aBewareRect1
=aTmpR1
;
1203 aBewareRect2
=aTmpR2
;
1204 } else if ((((bRts1
&& aBewareRect1
.Right ()>aBewareRect2
.Left ()) ||
1205 (bLks1
&& aBewareRect1
.Left ()<aBewareRect2
.Right ())) &&
1206 ((bUnt2
&& aBewareRect2
.Bottom()>aBewareRect1
.Top ()) ||
1207 (bObn2
&& aBewareRect2
.Top ()<aBewareRect1
.Bottom()))) ||
1208 (((bRts2
&& aBewareRect2
.Right ()>aBewareRect1
.Left ()) ||
1209 (bLks2
&& aBewareRect2
.Left ()<aBewareRect1
.Right ())) &&
1210 ((bUnt1
&& aBewareRect1
.Bottom()>aBewareRect2
.Top ()) ||
1211 (bObn1
&& aBewareRect1
.Top ()<aBewareRect2
.Bottom())))) {
1213 if (bRts1
|| bRts2
) { aMeeting
.X()=nXMax
; }
1214 if (bLks1
|| bLks2
) { aMeeting
.X()=nXMin
; }
1215 if (bUnt1
|| bUnt2
) { aMeeting
.Y()=nYMax
; }
1216 if (bObn1
|| bObn2
) { aMeeting
.Y()=nYMin
; }
1221 XPolygon
aXP1(ImpCalcObjToCenter(aPt1
,nAngle1
,aBewareRect1
,aMeeting
));
1222 XPolygon
aXP2(ImpCalcObjToCenter(aPt2
,nAngle2
,aBewareRect2
,aMeeting
));
1223 sal_uInt16 nXP1Anz
=aXP1
.GetPointCount();
1224 sal_uInt16 nXP2Anz
=aXP2
.GetPointCount();
1226 pInfo
->nObj1Lines
=nXP1Anz
; if (nXP1Anz
>1) pInfo
->nObj1Lines
--;
1227 pInfo
->nObj2Lines
=nXP2Anz
; if (nXP2Anz
>1) pInfo
->nObj2Lines
--;
1229 Point
aEP1(aXP1
[nXP1Anz
-1]);
1230 Point
aEP2(aXP2
[nXP2Anz
-1]);
1231 bool bInsMeetingPoint
=aEP1
.X()!=aEP2
.X() && aEP1
.Y()!=aEP2
.Y();
1232 bool bHorzE1
=aEP1
.Y()==aXP1
[nXP1Anz
-2].Y(); // is last line of XP1 horizontal?
1233 bool bHorzE2
=aEP2
.Y()==aXP2
[nXP2Anz
-2].Y(); // is last line of XP2 horizontal?
1234 if (aEP1
==aEP2
&& ((bHorzE1
&& bHorzE2
&& aEP1
.Y()==aEP2
.Y()) || (!bHorzE1
&& !bHorzE2
&& aEP1
.X()==aEP2
.X()))) {
1235 // special casing 'I' connectors
1236 nXP1Anz
--; aXP1
.Remove(nXP1Anz
,1);
1237 nXP2Anz
--; aXP2
.Remove(nXP2Anz
,1);
1239 if (bInsMeetingPoint
) {
1240 aXP1
.Insert(XPOLY_APPEND
,aMeeting
,XPOLY_NORMAL
);
1242 // Inserting a MeetingPoint adds 2 new lines,
1243 // either might become the center line.
1244 if (pInfo
->nObj1Lines
==pInfo
->nObj2Lines
) {
1245 pInfo
->nObj1Lines
++;
1246 pInfo
->nObj2Lines
++;
1248 if (pInfo
->nObj1Lines
>pInfo
->nObj2Lines
) {
1249 pInfo
->nObj2Lines
++;
1250 pInfo
->nMiddleLine
=nXP1Anz
-1;
1252 pInfo
->nObj1Lines
++;
1253 pInfo
->nMiddleLine
=nXP1Anz
;
1257 } else if (bInfo
&& aEP1
!=aEP2
&& nXP1Anz
+nXP2Anz
>=4) {
1258 // By connecting both ends, another line is added, this becomes the center line.
1259 pInfo
->nMiddleLine
=nXP1Anz
-1;
1261 sal_uInt16 nNum
=aXP2
.GetPointCount();
1262 if (aXP1
[nXP1Anz
-1]==aXP2
[nXP2Anz
-1] && nXP1Anz
>1 && nXP2Anz
>1) nNum
--;
1265 aXP1
.Insert(XPOLY_APPEND
,aXP2
[nNum
],XPOLY_NORMAL
);
1267 sal_uInt16 nPntAnz
=aXP1
.GetPointCount();
1269 if (bInfo
|| pnQuality
!=NULL
) {
1271 if (nPntAnz
==2) cForm
='I';
1272 else if (nPntAnz
==3) cForm
='L';
1273 else if (nPntAnz
==4) { // Z or U
1274 if (nAngle1
==nAngle2
) cForm
='U';
1276 } else if (nPntAnz
==6) { // S or C or ...
1277 if (nAngle1
!=nAngle2
) {
1278 // For type S, line 2 has the same direction as line 4.
1279 // For type C, the opposite is true.
1284 if (aP1
.Y()==aP2
.Y()) { // else both lines are horizontal
1285 if ((aP1
.X()<aP2
.X())==(aP3
.X()<aP4
.X())) cForm
='S';
1287 } else { // else both lines are vertical
1288 if ((aP1
.Y()<aP2
.Y())==(aP3
.Y()<aP4
.Y())) cForm
='S';
1291 } else cForm
='4'; // else is case 3 with 5 lines
1292 } else cForm
='?'; //
1295 pInfo
->cOrthoForm
=cForm
;
1296 if (cForm
=='I' || cForm
=='L' || cForm
=='Z' || cForm
=='U') {
1297 pInfo
->nObj1Lines
=1;
1298 pInfo
->nObj2Lines
=1;
1299 if (cForm
=='Z' || cForm
=='U') {
1300 pInfo
->nMiddleLine
=1;
1302 pInfo
->nMiddleLine
=0xFFFF;
1304 } else if (cForm
=='S' || cForm
=='C') {
1305 pInfo
->nObj1Lines
=2;
1306 pInfo
->nObj2Lines
=2;
1307 pInfo
->nMiddleLine
=2;
1311 if (pnQuality
!=NULL
) {
1312 sal_uIntPtr nQual
=0;
1313 sal_uIntPtr nQual0
=nQual
; // prevent overruns
1314 bool bOverflow
= false;
1315 Point
aPt0(aXP1
[0]);
1316 for (sal_uInt16 nPntNum
=1; nPntNum
<nPntAnz
; nPntNum
++) {
1317 Point
aPt1b(aXP1
[nPntNum
]);
1318 nQual
+=std::abs(aPt1b
.X()-aPt0
.X())+std::abs(aPt1b
.Y()-aPt0
.Y());
1319 if (nQual
<nQual0
) bOverflow
= true;
1324 sal_uInt16 nTmp
=nPntAnz
;
1326 nTmp
=2; // Z shape with good quality (nTmp=2 instead of 4)
1327 sal_uIntPtr n1
=std::abs(aXP1
[1].X()-aXP1
[0].X())+std::abs(aXP1
[1].Y()-aXP1
[0].Y());
1328 sal_uIntPtr n2
=std::abs(aXP1
[2].X()-aXP1
[1].X())+std::abs(aXP1
[2].Y()-aXP1
[1].Y());
1329 sal_uIntPtr n3
=std::abs(aXP1
[3].X()-aXP1
[2].X())+std::abs(aXP1
[3].Y()-aXP1
[2].Y());
1330 // try to make lines lengths similar
1331 sal_uIntPtr nBesser
=0;
1334 if (n1
>=n2
) nBesser
=6;
1335 else if (n1
>=3*n3
) nBesser
=4;
1336 else if (n1
>=2*n3
) nBesser
=2;
1337 if (aXP1
[0].Y()!=aXP1
[1].Y()) nBesser
++; // vertical starting line gets a plus (for H/V-Prio)
1338 if (nQual
>nBesser
) nQual
-=nBesser
; else nQual
=0;
1342 nQual
+=(sal_uIntPtr
)nTmp
*0x01000000;
1343 if (nQual
<nQual0
|| nTmp
>15) bOverflow
= true;
1345 if (nPntAnz
>=2) { // check exit angle again
1346 Point
aP1(aXP1
[1]); aP1
-=aXP1
[0];
1347 Point
aP2(aXP1
[nPntAnz
-2]); aP2
-=aXP1
[nPntAnz
-1];
1348 long nAng1
=0; if (aP1
.X()<0) nAng1
=18000; if (aP1
.Y()>0) nAng1
=27000;
1349 if (aP1
.Y()<0) nAng1
=9000; if (aP1
.X()!=0 && aP1
.Y()!=0) nAng1
=1; // slant?!
1350 long nAng2
=0; if (aP2
.X()<0) nAng2
=18000; if (aP2
.Y()>0) nAng2
=27000;
1351 if (aP2
.Y()<0) nAng2
=9000; if (aP2
.X()!=0 && aP2
.Y()!=0) nAng2
=1; // slant?!
1352 if (nAng1
!=nAngle1
) nIntersections
++;
1353 if (nAng2
!=nAngle2
) nIntersections
++;
1356 // For the quality check, use the original Rects and at the same time
1357 // check whether one them was scaled down for the calculation of the
1358 // Edges (e. g. case 2.9)
1359 aBewareRect1
=rBewareRect1
;
1360 aBewareRect2
=rBewareRect2
;
1362 for (sal_uInt16 i
=0; i
<nPntAnz
; i
++) {
1363 Point
aPt1b(aXP1
[i
]);
1364 bool b1
=aPt1b
.X()>aBewareRect1
.Left() && aPt1b
.X()<aBewareRect1
.Right() &&
1365 aPt1b
.Y()>aBewareRect1
.Top() && aPt1b
.Y()<aBewareRect1
.Bottom();
1366 bool b2
=aPt1b
.X()>aBewareRect2
.Left() && aPt1b
.X()<aBewareRect2
.Right() &&
1367 aPt1b
.Y()>aBewareRect2
.Top() && aPt1b
.Y()<aBewareRect2
.Bottom();
1368 sal_uInt16 nInt0
=nIntersections
;
1369 if (i
==0 || i
==nPntAnz
-1) {
1370 if (b1
&& b2
) nIntersections
++;
1372 if (b1
) nIntersections
++;
1373 if (b2
) nIntersections
++;
1375 // check for overlaps
1376 if (i
>0 && nInt0
==nIntersections
) {
1377 if (aPt0
.Y()==aPt1b
.Y()) { // horizontal line
1378 if (aPt0
.Y()>aBewareRect1
.Top() && aPt0
.Y()<aBewareRect1
.Bottom() &&
1379 ((aPt0
.X()<=aBewareRect1
.Left() && aPt1b
.X()>=aBewareRect1
.Right()) ||
1380 (aPt1b
.X()<=aBewareRect1
.Left() && aPt0
.X()>=aBewareRect1
.Right()))) nIntersections
++;
1381 if (aPt0
.Y()>aBewareRect2
.Top() && aPt0
.Y()<aBewareRect2
.Bottom() &&
1382 ((aPt0
.X()<=aBewareRect2
.Left() && aPt1b
.X()>=aBewareRect2
.Right()) ||
1383 (aPt1b
.X()<=aBewareRect2
.Left() && aPt0
.X()>=aBewareRect2
.Right()))) nIntersections
++;
1384 } else { // vertical line
1385 if (aPt0
.X()>aBewareRect1
.Left() && aPt0
.X()<aBewareRect1
.Right() &&
1386 ((aPt0
.Y()<=aBewareRect1
.Top() && aPt1b
.Y()>=aBewareRect1
.Bottom()) ||
1387 (aPt1b
.Y()<=aBewareRect1
.Top() && aPt0
.Y()>=aBewareRect1
.Bottom()))) nIntersections
++;
1388 if (aPt0
.X()>aBewareRect2
.Left() && aPt0
.X()<aBewareRect2
.Right() &&
1389 ((aPt0
.Y()<=aBewareRect2
.Top() && aPt1b
.Y()>=aBewareRect2
.Bottom()) ||
1390 (aPt1b
.Y()<=aBewareRect2
.Top() && aPt0
.Y()>=aBewareRect2
.Bottom()))) nIntersections
++;
1395 if (nPntAnz
<=1) nIntersections
++;
1397 nQual
+=(sal_uIntPtr
)nIntersections
*0x10000000;
1398 if (nQual
<nQual0
|| nIntersections
>15) bOverflow
= true;
1400 if (bOverflow
|| nQual
==0xFFFFFFFF) nQual
=0xFFFFFFFE;
1403 if (bInfo
) { // now apply line offsets to aXP1
1404 if (pInfo
->nMiddleLine
!=0xFFFF) {
1405 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(MIDDLELINE
,aXP1
);
1406 if (pInfo
->ImpIsHorzLine(MIDDLELINE
,aXP1
)) {
1407 aXP1
[nIdx
].Y()+=pInfo
->aMiddleLine
.Y();
1408 aXP1
[nIdx
+1].Y()+=pInfo
->aMiddleLine
.Y();
1410 aXP1
[nIdx
].X()+=pInfo
->aMiddleLine
.X();
1411 aXP1
[nIdx
+1].X()+=pInfo
->aMiddleLine
.X();
1414 if (pInfo
->nObj1Lines
>=2) {
1415 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(OBJ1LINE2
,aXP1
);
1416 if (pInfo
->ImpIsHorzLine(OBJ1LINE2
,aXP1
)) {
1417 aXP1
[nIdx
].Y()+=pInfo
->aObj1Line2
.Y();
1418 aXP1
[nIdx
+1].Y()+=pInfo
->aObj1Line2
.Y();
1420 aXP1
[nIdx
].X()+=pInfo
->aObj1Line2
.X();
1421 aXP1
[nIdx
+1].X()+=pInfo
->aObj1Line2
.X();
1424 if (pInfo
->nObj1Lines
>=3) {
1425 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(OBJ1LINE3
,aXP1
);
1426 if (pInfo
->ImpIsHorzLine(OBJ1LINE3
,aXP1
)) {
1427 aXP1
[nIdx
].Y()+=pInfo
->aObj1Line3
.Y();
1428 aXP1
[nIdx
+1].Y()+=pInfo
->aObj1Line3
.Y();
1430 aXP1
[nIdx
].X()+=pInfo
->aObj1Line3
.X();
1431 aXP1
[nIdx
+1].X()+=pInfo
->aObj1Line3
.X();
1434 if (pInfo
->nObj2Lines
>=2) {
1435 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(OBJ2LINE2
,aXP1
);
1436 if (pInfo
->ImpIsHorzLine(OBJ2LINE2
,aXP1
)) {
1437 aXP1
[nIdx
].Y()+=pInfo
->aObj2Line2
.Y();
1438 aXP1
[nIdx
+1].Y()+=pInfo
->aObj2Line2
.Y();
1440 aXP1
[nIdx
].X()+=pInfo
->aObj2Line2
.X();
1441 aXP1
[nIdx
+1].X()+=pInfo
->aObj2Line2
.X();
1444 if (pInfo
->nObj2Lines
>=3) {
1445 sal_uInt16 nIdx
=pInfo
->ImpGetPolyIdx(OBJ2LINE3
,aXP1
);
1446 if (pInfo
->ImpIsHorzLine(OBJ2LINE3
,aXP1
)) {
1447 aXP1
[nIdx
].Y()+=pInfo
->aObj2Line3
.Y();
1448 aXP1
[nIdx
+1].Y()+=pInfo
->aObj2Line3
.Y();
1450 aXP1
[nIdx
].X()+=pInfo
->aObj2Line3
.X();
1451 aXP1
[nIdx
+1].X()+=pInfo
->aObj2Line3
.X();
1455 // make the connector a bezier curve, if appropriate
1456 if (eKind
==SDREDGE_BEZIER
&& nPntAnz
>2) {
1457 Point
* pPt1
=&aXP1
[0];
1458 Point
* pPt2
=&aXP1
[1];
1459 Point
* pPt3
=&aXP1
[nPntAnz
-2];
1460 Point
* pPt4
=&aXP1
[nPntAnz
-1];
1461 long dx1
=pPt2
->X()-pPt1
->X();
1462 long dy1
=pPt2
->Y()-pPt1
->Y();
1463 long dx2
=pPt3
->X()-pPt4
->X();
1464 long dy2
=pPt3
->Y()-pPt4
->Y();
1465 if (cForm
=='L') { // nPntAnz==3
1466 aXP1
.SetFlags(1,XPOLY_CONTROL
);
1468 aXP1
.Insert(2,aPt3
,XPOLY_CONTROL
);
1469 nPntAnz
=aXP1
.GetPointCount();
1471 pPt3
=&aXP1
[nPntAnz
-2];
1476 } else if (nPntAnz
>=4 && nPntAnz
<=6) { // Z or U or ...
1477 // To all others, the end points of the original lines become control
1478 // points for now. Thus, we need to do some more work for nPntAnz>4!
1479 aXP1
.SetFlags(1,XPOLY_CONTROL
);
1480 aXP1
.SetFlags(nPntAnz
-2,XPOLY_CONTROL
);
1487 // add a control point before and after center
1488 Point
aCenter(aXP1
[2]);
1489 long dx1b
=aCenter
.X()-aXP1
[1].X();
1490 long dy1b
=aCenter
.Y()-aXP1
[1].Y();
1491 long dx2b
=aCenter
.X()-aXP1
[3].X();
1492 long dy2b
=aCenter
.Y()-aXP1
[3].Y();
1493 aXP1
.Insert(2,aCenter
,XPOLY_CONTROL
);
1494 aXP1
.SetFlags(3,XPOLY_SYMMTR
);
1495 aXP1
.Insert(4,aCenter
,XPOLY_CONTROL
);
1496 aXP1
[2].X()-=dx1b
/2;
1497 aXP1
[2].Y()-=dy1b
/2;
1498 aXP1
[3].X()-=(dx1b
+dx2b
)/4;
1499 aXP1
[3].Y()-=(dy1b
+dy2b
)/4;
1500 aXP1
[4].X()-=dx2b
/2;
1501 aXP1
[4].Y()-=dy2b
/2;
1504 Point
aPt1b(aXP1
[2]);
1505 Point
aPt2b(aXP1
[3]);
1506 aXP1
.Insert(2,aPt1b
,XPOLY_CONTROL
);
1507 aXP1
.Insert(5,aPt2b
,XPOLY_CONTROL
);
1508 long dx
=aPt1b
.X()-aPt2b
.X();
1509 long dy
=aPt1b
.Y()-aPt2b
.Y();
1512 aXP1
.SetFlags(3,XPOLY_SYMMTR
);
1513 aXP1
.Remove(4,1); // because it's identical with aXP1[3]
1521 There could be a maximum of 64 different developments with with 5 lines, a
1522 maximum of 32 developments with 4 lines, a maximum of 16 developments with
1523 3 lines, a maximum of 8 developments with 2 lines.
1524 This gives us a total of 124 possibilities.
1525 Normalized for the 1st exit angle to the right, there remain 31 possibilities.
1526 Now, normalizing away the vertical mirroring, we get to a total of 16
1527 characteristic developments with 1 through 5 lines:
1529 1 line (type "I") --
1531 2 lines (type "L") __|
1533 3 lines (type "U") __ (type "Z") _
1536 4 lines #1 _| #2 | | #3 |_ #4 | |
1538 Of these, #1 is implausible, #2 is a rotated version of #3. This leaves
1539 #2 (from now on referred to as 4.1) and #4 (from now on referred to as 4.2).
1541 5 lines #1 _| #2 _| #3 ___ #4 _
1544 #5 |_ #6 |_ #7 _| | #8 ____
1546 Of these, 5.1, 5.2, 5.4 and 5.5 are implausible, 5.7 is a reversed version
1547 of 5.3. This leaves 5.3 (type "4"), 5.6 (type "S") and 5.8 (type "C").
1549 We now have discerned the 9 basic types to cover all 400 possible constellations
1550 of object positions and exit angles. 4 of the 9 types have got a center
1551 line (CL). The number of object margins per object varies between 0 and 3:
1558 4.2: y 0 1 = U+1, respectively 1+U
1559 4.4: n 0-2 0-2 = Z+1
1562 "C": n 0-3 0-3 = 1+U+1
1565 void SdrEdgeObj::Notify(SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
1567 SfxSimpleHint
* pSimple
=PTR_CAST(SfxSimpleHint
,&rHint
);
1568 sal_uIntPtr nId
=pSimple
==0 ? 0 : pSimple
->GetId();
1569 bool bDataChg
=nId
==SFX_HINT_DATACHANGED
;
1570 bool bDying
=nId
==SFX_HINT_DYING
;
1571 bool bObj1
=aCon1
.pObj
!=NULL
&& aCon1
.pObj
->GetBroadcaster()==&rBC
;
1572 bool bObj2
=aCon2
.pObj
!=NULL
&& aCon2
.pObj
->GetBroadcaster()==&rBC
;
1573 if (bDying
&& (bObj1
|| bObj2
)) {
1574 // catch Dying, so AttrObj doesn't start broadcasting
1575 // about an alleged change of template
1576 if (bObj1
) aCon1
.pObj
=NULL
;
1577 if (bObj2
) aCon2
.pObj
=NULL
;
1580 if ( bObj1
|| bObj2
)
1582 bEdgeTrackUserDefined
= sal_False
;
1584 SdrTextObj::Notify(rBC
,rHint
);
1585 if (nNotifyingCount
==0) { // a locking flag
1586 ((SdrEdgeObj
*)this)->nNotifyingCount
++;
1587 SdrHint
* pSdrHint
=PTR_CAST(SdrHint
,&rHint
);
1588 if (bDataChg
) { // StyleSheet changed
1589 ImpSetAttrToEdgeInfo(); // when changing templates, copy values from Pool to aEdgeInfo
1592 (bObj1
&& aCon1
.pObj
->GetPage()==pPage
) ||
1593 (bObj2
&& aCon2
.pObj
->GetPage()==pPage
) ||
1594 (pSdrHint
&& pSdrHint
->GetKind()==HINT_OBJREMOVED
))
1596 // broadcasting only, if on the same page
1597 Rectangle aBoundRect0
; if (pUserCall
!=NULL
) aBoundRect0
=GetCurrentBoundRect();
1598 ImpDirtyEdgeTrack();
1600 // only redraw here, object hasn't actually changed
1603 SendUserCall(SDRUSERCALL_RESIZE
,aBoundRect0
);
1605 ((SdrEdgeObj
*)this)->nNotifyingCount
--;
1609 /** updates edges that are connected to the edges of this object
1610 as if the connected objects sent a repaint broadcast
1612 void SdrEdgeObj::Reformat()
1614 if( NULL
!= aCon1
.pObj
)
1616 SfxSimpleHint
aHint( SFX_HINT_DATACHANGED
);
1617 Notify( *const_cast<SfxBroadcaster
*>(aCon1
.pObj
->GetBroadcaster()), aHint
);
1620 if( NULL
!= aCon2
.pObj
)
1622 SfxSimpleHint
aHint( SFX_HINT_DATACHANGED
);
1623 Notify( *const_cast<SfxBroadcaster
*>(aCon2
.pObj
->GetBroadcaster()), aHint
);
1627 SdrEdgeObj
* SdrEdgeObj::Clone() const
1629 return CloneHelper
< SdrEdgeObj
>();
1632 SdrEdgeObj
& SdrEdgeObj::operator=(const SdrEdgeObj
& rObj
)
1636 SdrTextObj::operator=(rObj
);
1637 *pEdgeTrack
=*rObj
.pEdgeTrack
;
1638 bEdgeTrackDirty
=rObj
.bEdgeTrackDirty
;
1643 aEdgeInfo
=rObj
.aEdgeInfo
;
1647 void SdrEdgeObj::TakeObjNameSingul(XubString
& rName
) const
1649 rName
=ImpGetResStr(STR_ObjNameSingulEDGE
);
1651 String
aName( GetName() );
1654 rName
+= sal_Unicode(' ');
1655 rName
+= sal_Unicode('\'');
1657 rName
+= sal_Unicode('\'');
1661 void SdrEdgeObj::TakeObjNamePlural(XubString
& rName
) const
1663 rName
=ImpGetResStr(STR_ObjNamePluralEDGE
);
1666 basegfx::B2DPolyPolygon
SdrEdgeObj::TakeXorPoly() const
1668 basegfx::B2DPolyPolygon aPolyPolygon
;
1670 if (bEdgeTrackDirty
)
1672 ((SdrEdgeObj
*)this)->ImpRecalcEdgeTrack();
1677 aPolyPolygon
.append(pEdgeTrack
->getB2DPolygon());
1680 return aPolyPolygon
;
1683 void SdrEdgeObj::SetEdgeTrackPath( const basegfx::B2DPolyPolygon
& rPoly
)
1685 if ( !rPoly
.count() )
1687 bEdgeTrackDirty
= sal_True
;
1688 bEdgeTrackUserDefined
= sal_False
;
1692 *pEdgeTrack
= XPolygon( rPoly
.getB2DPolygon( 0 ) );
1693 bEdgeTrackDirty
= sal_False
;
1694 bEdgeTrackUserDefined
= sal_True
;
1696 // #i110629# also set aRect and maSnapeRect depending on pEdgeTrack
1697 const Rectangle
aPolygonBounds(pEdgeTrack
->GetBoundRect());
1698 aRect
= aPolygonBounds
;
1699 maSnapRect
= aPolygonBounds
;
1703 basegfx::B2DPolyPolygon
SdrEdgeObj::GetEdgeTrackPath() const
1705 basegfx::B2DPolyPolygon aPolyPolygon
;
1707 if (bEdgeTrackDirty
)
1708 ((SdrEdgeObj
*)this)->ImpRecalcEdgeTrack();
1710 aPolyPolygon
.append( pEdgeTrack
->getB2DPolygon() );
1712 return aPolyPolygon
;
1715 sal_uInt32
SdrEdgeObj::GetHdlCount() const
1717 SdrEdgeKind eKind
=((SdrEdgeKindItem
&)(GetObjectItem(SDRATTR_EDGEKIND
))).GetValue();
1718 sal_uInt32
nHdlAnz(0L);
1719 sal_uInt32
nPntAnz(pEdgeTrack
->GetPointCount());
1725 if ((eKind
==SDREDGE_ORTHOLINES
|| eKind
==SDREDGE_BEZIER
) && nPntAnz
>= 4L)
1727 sal_uInt32
nO1(aEdgeInfo
.nObj1Lines
> 0L ? aEdgeInfo
.nObj1Lines
- 1L : 0L);
1728 sal_uInt32
nO2(aEdgeInfo
.nObj2Lines
> 0L ? aEdgeInfo
.nObj2Lines
- 1L : 0L);
1729 sal_uInt32
nM(aEdgeInfo
.nMiddleLine
!= 0xFFFF ? 1L : 0L);
1730 nHdlAnz
+= nO1
+ nO2
+ nM
;
1732 else if (eKind
==SDREDGE_THREELINES
&& nPntAnz
== 4L)
1734 if(GetConnectedNode(sal_True
))
1737 if(GetConnectedNode(sal_False
))
1745 SdrHdl
* SdrEdgeObj::GetHdl(sal_uInt32 nHdlNum
) const
1748 sal_uInt32
nPntAnz(pEdgeTrack
->GetPointCount());
1751 pHdl
=new ImpEdgeHdl((*pEdgeTrack
)[0],HDL_POLY
);
1752 if (aCon1
.pObj
!=NULL
&& aCon1
.bBestVertex
) pHdl
->Set1PixMore(sal_True
);
1753 } else if (nHdlNum
==1) {
1754 pHdl
=new ImpEdgeHdl((*pEdgeTrack
)[sal_uInt16(nPntAnz
-1)],HDL_POLY
);
1755 if (aCon2
.pObj
!=NULL
&& aCon2
.bBestVertex
) pHdl
->Set1PixMore(sal_True
);
1757 SdrEdgeKind eKind
=((SdrEdgeKindItem
&)(GetObjectItem(SDRATTR_EDGEKIND
))).GetValue();
1758 if (eKind
==SDREDGE_ORTHOLINES
|| eKind
==SDREDGE_BEZIER
) {
1759 sal_uInt32
nO1(aEdgeInfo
.nObj1Lines
> 0L ? aEdgeInfo
.nObj1Lines
- 1L : 0L);
1760 sal_uInt32
nO2(aEdgeInfo
.nObj2Lines
> 0L ? aEdgeInfo
.nObj2Lines
- 1L : 0L);
1761 sal_uInt32
nM(aEdgeInfo
.nMiddleLine
!= 0xFFFF ? 1L : 0L);
1762 sal_uInt32
nNum(nHdlNum
- 2L);
1764 pHdl
=new ImpEdgeHdl(Point(),HDL_POLY
);
1767 if (nNum
==0) ((ImpEdgeHdl
*)pHdl
)->SetLineCode(OBJ1LINE2
);
1768 if (nNum
==1) ((ImpEdgeHdl
*)pHdl
)->SetLineCode(OBJ1LINE3
);
1773 if (nNum
==0) ((ImpEdgeHdl
*)pHdl
)->SetLineCode(OBJ2LINE2
);
1774 if (nNum
==1) ((ImpEdgeHdl
*)pHdl
)->SetLineCode(OBJ2LINE3
);
1778 nPt
=aEdgeInfo
.nMiddleLine
;
1779 ((ImpEdgeHdl
*)pHdl
)->SetLineCode(MIDDLELINE
);
1784 Point
aPos((*pEdgeTrack
)[(sal_uInt16
)nPt
]);
1785 aPos
+=(*pEdgeTrack
)[(sal_uInt16
)nPt
+1];
1793 } else if (eKind
==SDREDGE_THREELINES
) {
1794 sal_uInt32
nNum(nHdlNum
);
1795 if (GetConnectedNode(sal_True
)==NULL
) nNum
++;
1796 Point
aPos((*pEdgeTrack
)[(sal_uInt16
)nNum
-1]);
1797 pHdl
=new ImpEdgeHdl(aPos
,HDL_POLY
);
1798 if (nNum
==2) ((ImpEdgeHdl
*)pHdl
)->SetLineCode(OBJ1LINE2
);
1799 if (nNum
==3) ((ImpEdgeHdl
*)pHdl
)->SetLineCode(OBJ2LINE2
);
1803 pHdl
->SetPointNum(nHdlNum
);
1809 ////////////////////////////////////////////////////////////////////////////////////////////////////
1811 bool SdrEdgeObj::hasSpecialDrag() const
1816 SdrObject
* SdrEdgeObj::getFullDragClone() const
1818 // use Clone operator
1819 SdrEdgeObj
* pRetval
= (SdrEdgeObj
*)Clone();
1821 // copy connections for clone, SdrEdgeObj::operator= does not do this
1822 pRetval
->ConnectToNode(true, GetConnectedNode(true));
1823 pRetval
->ConnectToNode(false, GetConnectedNode(false));
1828 bool SdrEdgeObj::beginSpecialDrag(SdrDragStat
& rDrag
) const
1833 rDrag
.SetEndDragChangesAttributes(true);
1835 if(rDrag
.GetHdl()->GetPointNum() < 2)
1837 rDrag
.SetNoSnap(true);
1843 bool SdrEdgeObj::applySpecialDrag(SdrDragStat
& rDragStat
)
1845 SdrEdgeObj
* pOriginalEdge
= dynamic_cast< SdrEdgeObj
* >(rDragStat
.GetHdl()->GetObj());
1846 const bool bOriginalEdgeModified(pOriginalEdge
== this);
1848 if(!bOriginalEdgeModified
&& pOriginalEdge
)
1850 // copy connections when clone is modified. This is needed because
1851 // as preparation to this modification the data from the original object
1852 // was copied to the clone using the operator=. As can be seen there,
1853 // that operator does not copy the connections (for good reason)
1854 ConnectToNode(true, pOriginalEdge
->GetConnection(true).GetObject());
1855 ConnectToNode(false, pOriginalEdge
->GetConnection(false).GetObject());
1858 if(rDragStat
.GetHdl()->GetPointNum() < 2)
1860 // start or end point connector drag
1861 const bool bDragA(0 == rDragStat
.GetHdl()->GetPointNum());
1862 const Point
aPointNow(rDragStat
.GetNow());
1864 if(rDragStat
.GetPageView())
1866 SdrObjConnection
* pDraggedOne(bDragA
? &aCon1
: &aCon2
);
1869 DisconnectFromNode(bDragA
);
1871 // look for new connection
1872 ImpFindConnector(aPointNow
, *rDragStat
.GetPageView(), *pDraggedOne
, pOriginalEdge
);
1874 if(pDraggedOne
->pObj
)
1876 // if found, officially connect to it; ImpFindConnector only
1878 SdrObject
* pNewConnection
= pDraggedOne
->pObj
;
1879 pDraggedOne
->pObj
= 0;
1880 ConnectToNode(bDragA
, pNewConnection
);
1883 if(rDragStat
.GetView() && !bOriginalEdgeModified
)
1885 // show IA helper, but only do this during IA, so not when the original
1886 // Edge gets modified in the last call
1887 rDragStat
.GetView()->SetConnectMarker(*pDraggedOne
, *rDragStat
.GetPageView());
1893 // change pEdgeTrack to modified position
1896 (*pEdgeTrack
)[0] = aPointNow
;
1900 (*pEdgeTrack
)[sal_uInt16(pEdgeTrack
->GetPointCount()-1)] = aPointNow
;
1904 // reset edge info's offsets, this is a end point drag
1905 aEdgeInfo
.aObj1Line2
= Point();
1906 aEdgeInfo
.aObj1Line3
= Point();
1907 aEdgeInfo
.aObj2Line2
= Point();
1908 aEdgeInfo
.aObj2Line3
= Point();
1909 aEdgeInfo
.aMiddleLine
= Point();
1913 // control point connector drag
1914 const ImpEdgeHdl
* pEdgeHdl
= (ImpEdgeHdl
*)rDragStat
.GetHdl();
1915 const SdrEdgeLineCode eLineCode
= pEdgeHdl
->GetLineCode();
1916 const Point
aDist(rDragStat
.GetNow() - rDragStat
.GetStart());
1917 sal_Int32
nDist(pEdgeHdl
->IsHorzDrag() ? aDist
.X() : aDist
.Y());
1919 nDist
+= aEdgeInfo
.ImpGetLineVersatz(eLineCode
, *pEdgeTrack
);
1920 aEdgeInfo
.ImpSetLineVersatz(eLineCode
, *pEdgeTrack
, nDist
);
1923 // force recalculation of EdgeTrack
1924 *pEdgeTrack
= ImpCalcEdgeTrack(*pEdgeTrack
, aCon1
, aCon2
, &aEdgeInfo
);
1925 bEdgeTrackDirty
=sal_False
;
1927 // save EdgeInfos and mark object as user modified
1928 ImpSetEdgeInfoToAttr();
1929 bEdgeTrackUserDefined
= false;
1933 if(bOriginalEdgeModified
&& rDragStat
.GetView())
1935 // hide connect marker helper again when original gets changed.
1936 // This happens at the end of the interaction
1937 rDragStat
.GetView()->HideConnectMarker();
1943 String
SdrEdgeObj::getSpecialDragComment(const SdrDragStat
& rDrag
) const
1945 const bool bCreateComment(rDrag
.GetView() && this == rDrag
.GetView()->GetCreateObj());
1954 ImpTakeDescriptionStr(STR_DragEdgeTail
, aStr
);
1960 ////////////////////////////////////////////////////////////////////////////////////////////////////
1962 basegfx::B2DPolygon
SdrEdgeObj::ImplAddConnectorOverlay(SdrDragMethod
& rDragMethod
, bool bTail1
, bool bTail2
, bool bDetail
) const
1964 basegfx::B2DPolygon aResult
;
1968 SdrObjConnection
aMyCon1(aCon1
);
1969 SdrObjConnection
aMyCon2(aCon2
);
1973 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aMyCon1
.aObjOfs
.X(), aMyCon1
.aObjOfs
.Y()));
1974 aMyCon1
.aObjOfs
.X() = basegfx::fround(aTemp
.getX());
1975 aMyCon1
.aObjOfs
.Y() = basegfx::fround(aTemp
.getY());
1980 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aMyCon2
.aObjOfs
.X(), aMyCon2
.aObjOfs
.Y()));
1981 aMyCon2
.aObjOfs
.X() = basegfx::fround(aTemp
.getX());
1982 aMyCon2
.aObjOfs
.Y() = basegfx::fround(aTemp
.getY());
1985 SdrEdgeInfoRec
aInfo(aEdgeInfo
);
1986 XPolygon
aXP(ImpCalcEdgeTrack(*pEdgeTrack
, aMyCon1
, aMyCon2
, &aInfo
));
1988 if(aXP
.GetPointCount())
1990 aResult
= aXP
.getB2DPolygon();
1995 Point
aPt1((*pEdgeTrack
)[0]);
1996 Point
aPt2((*pEdgeTrack
)[sal_uInt16(pEdgeTrack
->GetPointCount() - 1)]);
1998 if (aCon1
.pObj
&& (aCon1
.bBestConn
|| aCon1
.bBestVertex
))
1999 aPt1
= aCon1
.pObj
->GetSnapRect().Center();
2001 if (aCon2
.pObj
&& (aCon2
.bBestConn
|| aCon2
.bBestVertex
))
2002 aPt2
= aCon2
.pObj
->GetSnapRect().Center();
2006 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aPt1
.X(), aPt1
.Y()));
2007 aPt1
.X() = basegfx::fround(aTemp
.getX());
2008 aPt1
.Y() = basegfx::fround(aTemp
.getY());
2013 const basegfx::B2DPoint
aTemp(rDragMethod
.getCurrentTransformation() * basegfx::B2DPoint(aPt2
.X(), aPt2
.Y()));
2014 aPt2
.X() = basegfx::fround(aTemp
.getX());
2015 aPt2
.Y() = basegfx::fround(aTemp
.getY());
2018 aResult
.append(basegfx::B2DPoint(aPt1
.X(), aPt1
.Y()));
2019 aResult
.append(basegfx::B2DPoint(aPt2
.X(), aPt2
.Y()));
2025 bool SdrEdgeObj::BegCreate(SdrDragStat
& rDragStat
)
2027 rDragStat
.SetNoSnap(sal_True
);
2028 pEdgeTrack
->SetPointCount(2);
2029 (*pEdgeTrack
)[0]=rDragStat
.GetStart();
2030 (*pEdgeTrack
)[1]=rDragStat
.GetNow();
2031 if (rDragStat
.GetPageView()!=NULL
) {
2032 ImpFindConnector(rDragStat
.GetStart(),*rDragStat
.GetPageView(),aCon1
,this);
2033 ConnectToNode(sal_True
,aCon1
.pObj
);
2035 *pEdgeTrack
=ImpCalcEdgeTrack(*pEdgeTrack
,aCon1
,aCon2
,&aEdgeInfo
);
2039 bool SdrEdgeObj::MovCreate(SdrDragStat
& rDragStat
)
2041 sal_uInt16 nMax
=pEdgeTrack
->GetPointCount();
2042 (*pEdgeTrack
)[nMax
-1]=rDragStat
.GetNow();
2043 if (rDragStat
.GetPageView()!=NULL
) {
2044 ImpFindConnector(rDragStat
.GetNow(),*rDragStat
.GetPageView(),aCon2
,this);
2045 rDragStat
.GetView()->SetConnectMarker(aCon2
,*rDragStat
.GetPageView());
2047 SetBoundRectDirty();
2048 bSnapRectDirty
=sal_True
;
2049 ConnectToNode(sal_False
,aCon2
.pObj
);
2050 *pEdgeTrack
=ImpCalcEdgeTrack(*pEdgeTrack
,aCon1
,aCon2
,&aEdgeInfo
);
2051 bEdgeTrackDirty
=sal_False
;
2055 bool SdrEdgeObj::EndCreate(SdrDragStat
& rDragStat
, SdrCreateCmd eCmd
)
2057 bool bOk
=(eCmd
==SDRCREATE_FORCEEND
|| rDragStat
.GetPointAnz()>=2);
2059 ConnectToNode(sal_True
,aCon1
.pObj
);
2060 ConnectToNode(sal_False
,aCon2
.pObj
);
2061 if (rDragStat
.GetView()!=NULL
) {
2062 rDragStat
.GetView()->HideConnectMarker();
2064 ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
2070 bool SdrEdgeObj::BckCreate(SdrDragStat
& rDragStat
)
2072 if (rDragStat
.GetView()!=NULL
) {
2073 rDragStat
.GetView()->HideConnectMarker();
2078 void SdrEdgeObj::BrkCreate(SdrDragStat
& rDragStat
)
2080 if (rDragStat
.GetView()!=NULL
) {
2081 rDragStat
.GetView()->HideConnectMarker();
2085 basegfx::B2DPolyPolygon
SdrEdgeObj::TakeCreatePoly(const SdrDragStat
& /*rStatDrag*/) const
2087 basegfx::B2DPolyPolygon aRetval
;
2088 aRetval
.append(pEdgeTrack
->getB2DPolygon());
2092 Pointer
SdrEdgeObj::GetCreatePointer() const
2094 return Pointer(POINTER_DRAW_CONNECT
);
2097 bool SdrEdgeObj::ImpFindConnector(const Point
& rPt
, const SdrPageView
& rPV
, SdrObjConnection
& rCon
, const SdrEdgeObj
* pThis
, OutputDevice
* pOut
)
2100 if (pOut
==NULL
) pOut
=rPV
.GetView().GetFirstOutputDevice();
2101 if (pOut
==NULL
) return sal_False
;
2102 SdrObjList
* pOL
=rPV
.GetObjList();
2103 const SetOfByte
& rVisLayer
=rPV
.GetVisibleLayers();
2104 // sensitive area of connectors is twice as large as the one of the handles
2105 sal_uInt16 nMarkHdSiz
=rPV
.GetView().GetMarkHdlSizePixel();
2106 Size
aHalfConSiz(nMarkHdSiz
,nMarkHdSiz
);
2107 aHalfConSiz
=pOut
->PixelToLogic(aHalfConSiz
);
2108 Rectangle
aMouseRect(rPt
,rPt
);
2109 aMouseRect
.Left() -=aHalfConSiz
.Width();
2110 aMouseRect
.Top() -=aHalfConSiz
.Height();
2111 aMouseRect
.Right() +=aHalfConSiz
.Width();
2112 aMouseRect
.Bottom()+=aHalfConSiz
.Height();
2113 sal_uInt16 nBoundHitTol
=(sal_uInt16
)aHalfConSiz
.Width()/2; if (nBoundHitTol
==0) nBoundHitTol
=1;
2114 sal_uIntPtr no
=pOL
->GetObjCount();
2116 SdrObjConnection aTestCon
;
2117 SdrObjConnection aBestCon
;
2119 while (no
>0 && !bFnd
) {
2120 // issue: group objects on different layers return LayerID=0!
2122 SdrObject
* pObj
=pOL
->GetObj(no
);
2123 if (rVisLayer
.IsSet(pObj
->GetLayer()) && pObj
->IsVisible() && // only visible objects
2124 (pThis
==NULL
|| pObj
!=(SdrObject
*)pThis
) && // don't connect it to itself
2127 Rectangle
aObjBound(pObj
->GetCurrentBoundRect());
2128 if (aObjBound
.IsOver(aMouseRect
)) {
2129 aTestCon
.ResetVars();
2130 bool bEdge
=HAS_BASE(SdrEdgeObj
,pObj
); // no BestCon for Edge
2131 // User-defined connectors have absolute priority.
2132 // After those come Vertex, Corner and center (Best), all prioritized equally.
2133 // Finally, a HitTest for the object.
2134 const SdrGluePointList
* pGPL
=pObj
->GetGluePointList();
2135 sal_uInt16 nConAnz
=pGPL
==NULL
? 0 : pGPL
->GetCount();
2136 sal_uInt16 nGesAnz
=nConAnz
+9;
2137 bool bUserFnd
= false;
2138 sal_uIntPtr nBestDist
=0xFFFFFFFF;
2139 for (sal_uInt16 i
=0; i
<nGesAnz
; i
++)
2141 bool bUser
=i
<nConAnz
;
2142 bool bVertex
=i
>=nConAnz
+0 && i
<nConAnz
+4;
2143 bool bCorner
=i
>=nConAnz
+4 && i
<nConAnz
+8;
2144 bool bCenter
=i
==nConAnz
+8;
2147 sal_uInt16 nConNum
=i
;
2149 const SdrGluePoint
& rGP
=(*pGPL
)[nConNum
];
2150 aConPos
=rGP
.GetAbsolutePos(*pObj
);
2151 nConNum
=rGP
.GetId();
2153 } else if (bVertex
&& !bUserFnd
) {
2154 nConNum
=nConNum
-nConAnz
;
2155 if (rPV
.GetView().IsAutoVertexConnectors()) {
2156 SdrGluePoint
aPt(pObj
->GetVertexGluePoint(nConNum
));
2157 aConPos
=aPt
.GetAbsolutePos(*pObj
);
2160 } else if (bCorner
&& !bUserFnd
) {
2162 if (rPV
.GetView().IsAutoCornerConnectors()) {
2163 SdrGluePoint
aPt(pObj
->GetCornerGluePoint(nConNum
));
2164 aConPos
=aPt
.GetAbsolutePos(*pObj
);
2168 else if (bCenter
&& !bUserFnd
&& !bEdge
)
2170 // Suppress default connect at object center
2171 if(!pThis
|| !pThis
->GetSuppressDefaultConnect())
2175 aConPos
=aObjBound
.Center();
2179 if (bOk
&& aMouseRect
.IsInside(aConPos
)) {
2180 if (bUser
) bUserFnd
= true;
2182 sal_uIntPtr nDist
=(sal_uIntPtr
)std::abs(aConPos
.X()-rPt
.X())+(sal_uIntPtr
)std::abs(aConPos
.Y()-rPt
.Y());
2183 if (nDist
<nBestDist
) {
2186 aTestCon
.nConId
=nConNum
;
2187 aTestCon
.bAutoCorner
=bCorner
;
2188 aTestCon
.bAutoVertex
=bVertex
;
2189 aTestCon
.bBestConn
=sal_False
; // bCenter;
2190 aTestCon
.bBestVertex
=bCenter
;
2194 // if no connector is hit, try HitTest again, for BestConnector (=bCenter)
2197 SdrObjectPrimitiveHit(*pObj
, rPt
, nBoundHitTol
, rPV
, &rVisLayer
, false))
2199 // Suppress default connect at object inside bound
2200 if(!pThis
|| !pThis
->GetSuppressDefaultConnect())
2204 aTestCon
.bBestConn
=sal_True
;
2208 Rectangle
aMouseRect2(rPt
,rPt
);
2209 aMouseRect
.Left() -=nBoundHitTol
;
2210 aMouseRect
.Top() -=nBoundHitTol
;
2211 aMouseRect
.Right() +=nBoundHitTol
;
2212 aMouseRect
.Bottom()+=nBoundHitTol
;
2213 aObjBound
.IsOver(aMouseRect2
);
2223 void SdrEdgeObj::NbcSetSnapRect(const Rectangle
& rRect
)
2225 const Rectangle
aOld(GetSnapRect());
2229 if(aRect
.IsEmpty() && 0 == pEdgeTrack
->GetPointCount())
2231 // #i110629# When initializing, do not scale on empty Rectangle; this
2232 // will mirror the underlying text object (!)
2238 long nMulX
= rRect
.Right() - rRect
.Left();
2239 long nDivX
= aOld
.Right() - aOld
.Left();
2240 long nMulY
= rRect
.Bottom() - rRect
.Top();
2241 long nDivY
= aOld
.Bottom() - aOld
.Top();
2242 if ( nDivX
== 0 ) { nMulX
= 1; nDivX
= 1; }
2243 if ( nDivY
== 0 ) { nMulY
= 1; nDivY
= 1; }
2244 Fraction
aX(nMulX
, nDivX
);
2245 Fraction
aY(nMulY
, nDivY
);
2246 NbcResize(aOld
.TopLeft(), aX
, aY
);
2247 NbcMove(Size(rRect
.Left() - aOld
.Left(), rRect
.Top() - aOld
.Top()));
2252 void SdrEdgeObj::NbcMove(const Size
& rSiz
)
2254 SdrTextObj::NbcMove(rSiz
);
2255 MoveXPoly(*pEdgeTrack
,rSiz
);
2258 void SdrEdgeObj::NbcResize(const Point
& rRefPnt
, const Fraction
& aXFact
, const Fraction
& aYFact
)
2260 SdrTextObj::NbcResize(rRefPnt
,aXFact
,aXFact
);
2261 ResizeXPoly(*pEdgeTrack
,rRefPnt
,aXFact
,aYFact
);
2263 // if resize is not from paste, forget user distances
2264 if(!GetModel()->IsPasteResize())
2266 aEdgeInfo
.aObj1Line2
= Point();
2267 aEdgeInfo
.aObj1Line3
= Point();
2268 aEdgeInfo
.aObj2Line2
= Point();
2269 aEdgeInfo
.aObj2Line3
= Point();
2270 aEdgeInfo
.aMiddleLine
= Point();
2274 // #i54102# added rotation support
2275 void SdrEdgeObj::NbcRotate(const Point
& rRef
, long nWink
, double sn
, double cs
)
2277 if(bEdgeTrackUserDefined
)
2279 // #i120437# special handling when track is imported, apply
2280 // transformation directly to imported track.
2281 SdrTextObj::NbcRotate(rRef
, nWink
, sn
, cs
);
2282 RotateXPoly(*pEdgeTrack
, rRef
, sn
, cs
);
2286 // handle start and end point if not connected
2287 bool bCon1
=aCon1
.pObj
!=NULL
&& aCon1
.pObj
->GetPage()==pPage
;
2288 bool bCon2
=aCon2
.pObj
!=NULL
&& aCon2
.pObj
->GetPage()==pPage
;
2290 if(!bCon1
&& pEdgeTrack
)
2292 RotatePoint((*pEdgeTrack
)[0],rRef
,sn
,cs
);
2293 ImpDirtyEdgeTrack();
2296 if(!bCon2
&& pEdgeTrack
)
2298 sal_uInt16 nPntAnz
= pEdgeTrack
->GetPointCount();
2299 RotatePoint((*pEdgeTrack
)[sal_uInt16(nPntAnz
-1)],rRef
,sn
,cs
);
2300 ImpDirtyEdgeTrack();
2305 // #i54102# added mirror support
2306 void SdrEdgeObj::NbcMirror(const Point
& rRef1
, const Point
& rRef2
)
2308 if(bEdgeTrackUserDefined
)
2310 // #i120437# special handling when track is imported, apply
2311 // transformation directly to imported track.
2312 SdrTextObj::NbcMirror(rRef1
, rRef2
);
2313 MirrorXPoly(*pEdgeTrack
, rRef1
, rRef2
);
2317 // handle start and end point if not connected
2318 bool bCon1
=aCon1
.pObj
!=NULL
&& aCon1
.pObj
->GetPage()==pPage
;
2319 bool bCon2
=aCon2
.pObj
!=NULL
&& aCon2
.pObj
->GetPage()==pPage
;
2321 if(!bCon1
&& pEdgeTrack
)
2323 MirrorPoint((*pEdgeTrack
)[0],rRef1
,rRef2
);
2324 ImpDirtyEdgeTrack();
2327 if(!bCon2
&& pEdgeTrack
)
2329 sal_uInt16 nPntAnz
= pEdgeTrack
->GetPointCount();
2330 MirrorPoint((*pEdgeTrack
)[sal_uInt16(nPntAnz
-1)],rRef1
,rRef2
);
2331 ImpDirtyEdgeTrack();
2336 // #i54102# added shear support
2337 void SdrEdgeObj::NbcShear(const Point
& rRef
, long nWink
, double tn
, bool bVShear
)
2339 if(bEdgeTrackUserDefined
)
2341 // #i120437# special handling when track is imported, apply
2342 // transformation directly to imported track.
2343 SdrTextObj::NbcShear(rRef
, nWink
, tn
, bVShear
);
2344 ShearXPoly(*pEdgeTrack
, rRef
, tn
, bVShear
);
2348 // handle start and end point if not connected
2349 bool bCon1
=aCon1
.pObj
!=NULL
&& aCon1
.pObj
->GetPage()==pPage
;
2350 bool bCon2
=aCon2
.pObj
!=NULL
&& aCon2
.pObj
->GetPage()==pPage
;
2352 if(!bCon1
&& pEdgeTrack
)
2354 ShearPoint((*pEdgeTrack
)[0],rRef
,tn
,bVShear
);
2355 ImpDirtyEdgeTrack();
2358 if(!bCon2
&& pEdgeTrack
)
2360 sal_uInt16 nPntAnz
= pEdgeTrack
->GetPointCount();
2361 ShearPoint((*pEdgeTrack
)[sal_uInt16(nPntAnz
-1)],rRef
,tn
,bVShear
);
2362 ImpDirtyEdgeTrack();
2367 SdrObject
* SdrEdgeObj::DoConvertToPolyObj(sal_Bool bBezier
, bool bAddText
) const
2369 basegfx::B2DPolyPolygon aPolyPolygon
;
2370 aPolyPolygon
.append(pEdgeTrack
->getB2DPolygon());
2371 SdrObject
* pRet
= ImpConvertMakeObj(aPolyPolygon
, sal_False
, bBezier
);
2375 pRet
= ImpConvertAddText(pRet
, bBezier
);
2381 sal_uInt32
SdrEdgeObj::GetSnapPointCount() const
2386 Point
SdrEdgeObj::GetSnapPoint(sal_uInt32 i
) const
2388 ((SdrEdgeObj
*)this)->ImpUndirtyEdgeTrack();
2389 sal_uInt16 nAnz
=pEdgeTrack
->GetPointCount();
2390 if (i
==0) return (*pEdgeTrack
)[0];
2391 else return (*pEdgeTrack
)[nAnz
-1];
2394 sal_Bool
SdrEdgeObj::IsPolyObj() const
2399 sal_uInt32
SdrEdgeObj::GetPointCount() const
2404 Point
SdrEdgeObj::GetPoint(sal_uInt32 i
) const
2406 ((SdrEdgeObj
*)this)->ImpUndirtyEdgeTrack();
2407 sal_uInt16 nAnz
=pEdgeTrack
->GetPointCount();
2409 return (*pEdgeTrack
)[0];
2411 return (*pEdgeTrack
)[nAnz
-1];
2414 void SdrEdgeObj::NbcSetPoint(const Point
& rPnt
, sal_uInt32 i
)
2416 // TODO: Need an implementation to connect differently.
2417 ImpUndirtyEdgeTrack();
2418 sal_uInt16 nAnz
=pEdgeTrack
->GetPointCount();
2420 (*pEdgeTrack
)[0]=rPnt
;
2422 (*pEdgeTrack
)[nAnz
-1]=rPnt
;
2423 SetEdgeTrackDirty();
2427 SdrEdgeObjGeoData::SdrEdgeObjGeoData()
2429 pEdgeTrack
=new XPolygon
;
2432 SdrEdgeObjGeoData::~SdrEdgeObjGeoData()
2437 SdrObjGeoData
* SdrEdgeObj::NewGeoData() const
2439 return new SdrEdgeObjGeoData
;
2442 void SdrEdgeObj::SaveGeoData(SdrObjGeoData
& rGeo
) const
2444 SdrTextObj::SaveGeoData(rGeo
);
2445 SdrEdgeObjGeoData
& rEGeo
=(SdrEdgeObjGeoData
&)rGeo
;
2448 *rEGeo
.pEdgeTrack
=*pEdgeTrack
;
2449 rEGeo
.bEdgeTrackDirty
=bEdgeTrackDirty
;
2450 rEGeo
.bEdgeTrackUserDefined
=bEdgeTrackUserDefined
;
2451 rEGeo
.aEdgeInfo
=aEdgeInfo
;
2454 void SdrEdgeObj::RestGeoData(const SdrObjGeoData
& rGeo
)
2456 SdrTextObj::RestGeoData(rGeo
);
2457 SdrEdgeObjGeoData
& rEGeo
=(SdrEdgeObjGeoData
&)rGeo
;
2458 if (aCon1
.pObj
!=rEGeo
.aCon1
.pObj
) {
2459 if (aCon1
.pObj
!=NULL
) aCon1
.pObj
->RemoveListener(*this);
2461 if (aCon1
.pObj
!=NULL
) aCon1
.pObj
->AddListener(*this);
2463 if (aCon2
.pObj
!=rEGeo
.aCon2
.pObj
) {
2464 if (aCon2
.pObj
!=NULL
) aCon2
.pObj
->RemoveListener(*this);
2466 if (aCon2
.pObj
!=NULL
) aCon2
.pObj
->AddListener(*this);
2468 *pEdgeTrack
=*rEGeo
.pEdgeTrack
;
2469 bEdgeTrackDirty
=rEGeo
.bEdgeTrackDirty
;
2470 bEdgeTrackUserDefined
=rEGeo
.bEdgeTrackUserDefined
;
2471 aEdgeInfo
=rEGeo
.aEdgeInfo
;
2474 Point
SdrEdgeObj::GetTailPoint( sal_Bool bTail
) const
2476 if( pEdgeTrack
&& pEdgeTrack
->GetPointCount()!=0)
2478 const XPolygon
& rTrack0
= *pEdgeTrack
;
2485 const sal_uInt16 nSiz
= rTrack0
.GetPointCount() - 1;
2486 return rTrack0
[nSiz
];
2492 return aOutRect
.TopLeft();
2494 return aOutRect
.BottomRight();
2499 void SdrEdgeObj::SetTailPoint( sal_Bool bTail
, const Point
& rPt
)
2501 ImpSetTailPoint( bTail
, rPt
);
2505 /** this method is used by the api to set a glue point for a connection
2506 nId == -1 : The best default point is automatically chosen
2507 0 <= nId <= 3 : One of the default points is chosen
2508 nId >= 4 : A user defined glue point is chosen
2510 void SdrEdgeObj::setGluePointIndex( sal_Bool bTail
, sal_Int32 nIndex
/* = -1 */ )
2512 Rectangle aBoundRect0
; if (pUserCall
!=NULL
) aBoundRect0
=GetCurrentBoundRect();
2514 SdrObjConnection
& rConn1
= GetConnection( bTail
);
2516 rConn1
.SetAutoVertex( nIndex
>= 0 && nIndex
<= 3 );
2517 rConn1
.SetBestConnection( nIndex
< 0 );
2518 rConn1
.SetBestVertex( nIndex
< 0 );
2522 nIndex
-= 3; // the start api index is 0, whereas the implementation in svx starts from 1
2524 // for user defined glue points we have
2525 // to get the id for this index first
2526 const SdrGluePointList
* pList
= rConn1
.GetObject() ? rConn1
.GetObject()->GetGluePointList() : NULL
;
2527 if( pList
== NULL
|| SDRGLUEPOINT_NOTFOUND
== pList
->FindGluePoint((sal_uInt16
)nIndex
) )
2530 else if( nIndex
< 0 )
2535 rConn1
.SetConnectorId( (sal_uInt16
)nIndex
);
2539 ImpRecalcEdgeTrack();
2542 /** this method is used by the api to return a glue point id for a connection.
2543 See setGluePointId for possible return values */
2544 sal_Int32
SdrEdgeObj::getGluePointIndex( sal_Bool bTail
)
2546 SdrObjConnection
& rConn1
= GetConnection( bTail
);
2548 if( !rConn1
.IsBestConnection() )
2550 nId
= rConn1
.GetConnectorId();
2551 if( !rConn1
.IsAutoVertex() )
2552 nId
+= 3; // the start api index is 0, whereas the implementation in svx starts from 1
2557 // Implementation was missing; edge track needs to be invalidated additionally.
2558 void SdrEdgeObj::NbcSetAnchorPos(const Point
& rPnt
)
2560 // call parent functionality
2561 SdrTextObj::NbcSetAnchorPos(rPnt
);
2563 // Additionally, invalidate edge track
2564 ImpDirtyEdgeTrack();
2567 sal_Bool
SdrEdgeObj::TRGetBaseGeometry(basegfx::B2DHomMatrix
& rMatrix
, basegfx::B2DPolyPolygon
& rPolyPolygon
) const
2569 // use base method from SdrObject, it's not rotatable and
2570 // a call to GetSnapRect() is used. That's what we need for Connector.
2571 return SdrObject::TRGetBaseGeometry(rMatrix
, rPolyPolygon
);
2574 void SdrEdgeObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::B2DPolyPolygon
& rPolyPolygon
)
2576 // where appropriate take care for existing connections. For now, just use the
2577 // implementation from SdrObject.
2578 SdrObject::TRSetBaseGeometry(rMatrix
, rPolyPolygon
);
2581 // for geometry access
2582 ::basegfx::B2DPolygon
SdrEdgeObj::getEdgeTrack() const
2586 const_cast< SdrEdgeObj
* >(this)->ImpRecalcEdgeTrack();
2591 return pEdgeTrack
->getB2DPolygon();
2595 return ::basegfx::B2DPolygon();
2599 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */