Version 3.6.0.2, tag libreoffice-3.6.0.2
[LibreOffice.git] / svx / source / svdraw / svdoedge.cxx
blob4312ae4c1136ee655a0d35bdf78d10d3145a5969
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include <svx/svdoedge.hxx>
31 #include <svx/xpool.hxx>
32 #include <svx/xpoly.hxx>
33 #include <svx/svdattrx.hxx>
34 #include <svx/svdpool.hxx>
35 #include <svx/svdmodel.hxx>
36 #include <svx/svdpage.hxx>
37 #include <svx/svdpagv.hxx>
38 #include <svx/svdview.hxx>
39 #include <svx/svddrag.hxx>
40 #include <svx/svddrgv.hxx>
41 #include "svddrgm1.hxx"
42 #include <svx/svdhdl.hxx>
43 #include <svx/svdtrans.hxx>
44 #include <svx/svdetc.hxx>
45 #include "svx/svdglob.hxx" // StringCache
46 #include "svx/svdstr.hrc" // the object's name
47 #include <svl/style.hxx>
48 #include <svl/smplhint.hxx>
49 #include <editeng/eeitem.hxx>
50 #include <svx/sdr/properties/connectorproperties.hxx>
51 #include <svx/sdr/contact/viewcontactofsdredgeobj.hxx>
52 #include <basegfx/polygon/b2dpolygon.hxx>
53 #include <basegfx/polygon/b2dpolygontools.hxx>
54 #include <basegfx/matrix/b2dhommatrix.hxx>
55 #include <svx/sdrhittesthelper.hxx>
57 ////////////////////////////////////////////////////////////////////////////////////////////////////
59 SdrObjConnection::~SdrObjConnection()
63 void SdrObjConnection::ResetVars()
65 pObj=NULL;
66 nConId=0;
67 nXDist=0;
68 nYDist=0;
69 bBestConn=sal_True;
70 bBestVertex=sal_True;
71 bXDistOvr=sal_False;
72 bYDistOvr=sal_False;
73 bAutoVertex=sal_False;
74 bAutoCorner=sal_False;
77 bool SdrObjConnection::TakeGluePoint(SdrGluePoint& rGP, bool bSetAbsPos) const
79 bool bRet = false;
80 if (pObj!=NULL) { // one object has to be docked already!
81 if (bAutoVertex) {
82 rGP=pObj->GetVertexGluePoint(nConId);
83 bRet = true;
84 } else if (bAutoCorner) {
85 rGP=pObj->GetCornerGluePoint(nConId);
86 bRet = true;
87 } else {
88 const SdrGluePointList* pGPL=pObj->GetGluePointList();
89 if (pGPL!=NULL) {
90 sal_uInt16 nNum=pGPL->FindGluePoint(nConId);
91 if (nNum!=SDRGLUEPOINT_NOTFOUND) {
92 rGP=(*pGPL)[nNum];
93 bRet = true;
98 if (bRet && bSetAbsPos) {
99 Point aPt(rGP.GetAbsolutePos(*pObj));
100 aPt+=aObjOfs;
101 rGP.SetPos(aPt);
103 return bRet;
106 Point& SdrEdgeInfoRec::ImpGetLineVersatzPoint(SdrEdgeLineCode eLineCode)
108 switch (eLineCode) {
109 case OBJ1LINE2 : return aObj1Line2;
110 case OBJ1LINE3 : return aObj1Line3;
111 case OBJ2LINE2 : return aObj2Line2;
112 case OBJ2LINE3 : return aObj2Line3;
113 case MIDDLELINE: return aMiddleLine;
114 } // switch
115 return aMiddleLine;
118 sal_uInt16 SdrEdgeInfoRec::ImpGetPolyIdx(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
120 switch (eLineCode) {
121 case OBJ1LINE2 : return 1;
122 case OBJ1LINE3 : return 2;
123 case OBJ2LINE2 : return rXP.GetPointCount()-3;
124 case OBJ2LINE3 : return rXP.GetPointCount()-4;
125 case MIDDLELINE: return nMiddleLine;
126 } // switch
127 return 0;
130 bool SdrEdgeInfoRec::ImpIsHorzLine(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
132 sal_uInt16 nIdx=ImpGetPolyIdx(eLineCode,rXP);
133 bool bHorz=nAngle1==0 || nAngle1==18000;
134 if (eLineCode==OBJ2LINE2 || eLineCode==OBJ2LINE3) {
135 nIdx=rXP.GetPointCount()-nIdx;
136 bHorz=nAngle2==0 || nAngle2==18000;
138 if ((nIdx & 1)==1) bHorz=!bHorz;
139 return bHorz;
142 void SdrEdgeInfoRec::ImpSetLineVersatz(SdrEdgeLineCode eLineCode, const XPolygon& rXP, long nVal)
144 Point& rPt=ImpGetLineVersatzPoint(eLineCode);
145 if (ImpIsHorzLine(eLineCode,rXP)) rPt.Y()=nVal;
146 else rPt.X()=nVal;
149 long SdrEdgeInfoRec::ImpGetLineVersatz(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
151 const Point& rPt=ImpGetLineVersatzPoint(eLineCode);
152 if (ImpIsHorzLine(eLineCode,rXP)) return rPt.Y();
153 else return rPt.X();
156 //////////////////////////////////////////////////////////////////////////////
157 // BaseProperties section
159 sdr::properties::BaseProperties* SdrEdgeObj::CreateObjectSpecificProperties()
161 return new sdr::properties::ConnectorProperties(*this);
164 //////////////////////////////////////////////////////////////////////////////
165 // DrawContact section
167 sdr::contact::ViewContact* SdrEdgeObj::CreateObjectSpecificViewContact()
169 return new sdr::contact::ViewContactOfSdrEdgeObj(*this);
172 //////////////////////////////////////////////////////////////////////////////
174 TYPEINIT1(SdrEdgeObj,SdrTextObj);
176 SdrEdgeObj::SdrEdgeObj()
177 : SdrTextObj(),
178 nNotifyingCount(0),
179 bEdgeTrackDirty(sal_False),
180 bEdgeTrackUserDefined(sal_False),
181 // Default is to allow default connects
182 mbSuppressDefaultConnect(sal_False),
183 mbBoundRectCalculationRunning(sal_False)
185 bClosedObj=sal_False;
186 bIsEdge=sal_True;
187 pEdgeTrack=new XPolygon;
191 SdrEdgeObj::~SdrEdgeObj()
193 DisconnectFromNode(sal_True);
194 DisconnectFromNode(sal_False);
195 delete pEdgeTrack;
198 void SdrEdgeObj::ImpSetAttrToEdgeInfo()
200 const SfxItemSet& rSet = GetObjectItemSet();
201 SdrEdgeKind eKind = ((SdrEdgeKindItem&)(rSet.Get(SDRATTR_EDGEKIND))).GetValue();
202 sal_Int32 nVal1 = ((SdrEdgeLine1DeltaItem&)rSet.Get(SDRATTR_EDGELINE1DELTA)).GetValue();
203 sal_Int32 nVal2 = ((SdrEdgeLine2DeltaItem&)rSet.Get(SDRATTR_EDGELINE2DELTA)).GetValue();
204 sal_Int32 nVal3 = ((SdrEdgeLine3DeltaItem&)rSet.Get(SDRATTR_EDGELINE3DELTA)).GetValue();
206 if(eKind == SDREDGE_ORTHOLINES || eKind == SDREDGE_BEZIER)
208 sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
209 sal_uInt16 n = 0;
211 if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
213 aEdgeInfo.ImpSetLineVersatz(OBJ1LINE2, *pEdgeTrack, nVals[n]);
214 n++;
217 if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
219 aEdgeInfo.ImpSetLineVersatz(OBJ1LINE3, *pEdgeTrack, nVals[n]);
220 n++;
223 if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
225 aEdgeInfo.ImpSetLineVersatz(MIDDLELINE, *pEdgeTrack, nVals[n]);
226 n++;
229 if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
231 aEdgeInfo.ImpSetLineVersatz(OBJ2LINE3, *pEdgeTrack, nVals[n]);
232 n++;
235 if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
237 aEdgeInfo.ImpSetLineVersatz(OBJ2LINE2, *pEdgeTrack, nVals[n]);
238 n++;
241 else if(eKind == SDREDGE_THREELINES)
243 sal_Bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
244 sal_Bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
246 if(bHor1)
248 aEdgeInfo.aObj1Line2.X() = nVal1;
250 else
252 aEdgeInfo.aObj1Line2.Y() = nVal1;
255 if(bHor2)
257 aEdgeInfo.aObj2Line2.X() = nVal2;
259 else
261 aEdgeInfo.aObj2Line2.Y() = nVal2;
265 ImpDirtyEdgeTrack();
268 void SdrEdgeObj::ImpSetEdgeInfoToAttr()
270 const SfxItemSet& rSet = GetObjectItemSet();
271 SdrEdgeKind eKind = ((SdrEdgeKindItem&)(rSet.Get(SDRATTR_EDGEKIND))).GetValue();
272 sal_Int32 nValAnz = ((SdrEdgeLineDeltaAnzItem&)rSet.Get(SDRATTR_EDGELINEDELTAANZ)).GetValue();
273 sal_Int32 nVal1 = ((SdrEdgeLine1DeltaItem&)rSet.Get(SDRATTR_EDGELINE1DELTA)).GetValue();
274 sal_Int32 nVal2 = ((SdrEdgeLine2DeltaItem&)rSet.Get(SDRATTR_EDGELINE2DELTA)).GetValue();
275 sal_Int32 nVal3 = ((SdrEdgeLine3DeltaItem&)rSet.Get(SDRATTR_EDGELINE3DELTA)).GetValue();
276 sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
277 sal_uInt16 n = 0;
279 if(eKind == SDREDGE_ORTHOLINES || eKind == SDREDGE_BEZIER)
281 if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
283 nVals[n] = aEdgeInfo.ImpGetLineVersatz(OBJ1LINE2, *pEdgeTrack);
284 n++;
287 if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
289 nVals[n] = aEdgeInfo.ImpGetLineVersatz(OBJ1LINE3, *pEdgeTrack);
290 n++;
293 if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
295 nVals[n] = aEdgeInfo.ImpGetLineVersatz(MIDDLELINE, *pEdgeTrack);
296 n++;
299 if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
301 nVals[n] = aEdgeInfo.ImpGetLineVersatz(OBJ2LINE3, *pEdgeTrack);
302 n++;
305 if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
307 nVals[n] = aEdgeInfo.ImpGetLineVersatz(OBJ2LINE2, *pEdgeTrack);
308 n++;
311 else if(eKind == SDREDGE_THREELINES)
313 sal_Bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
314 sal_Bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
316 n = 2;
317 nVals[0] = bHor1 ? aEdgeInfo.aObj1Line2.X() : aEdgeInfo.aObj1Line2.Y();
318 nVals[1] = bHor2 ? aEdgeInfo.aObj2Line2.X() : aEdgeInfo.aObj2Line2.Y();
321 if(n != nValAnz || nVals[0] != nVal1 || nVals[1] != nVal2 || nVals[2] != nVal3)
323 // Here no more notifying is necessary, just local changes are OK.
324 if(n != nValAnz)
326 GetProperties().SetObjectItemDirect(SdrEdgeLineDeltaAnzItem(n));
329 if(nVals[0] != nVal1)
331 GetProperties().SetObjectItemDirect(SdrEdgeLine1DeltaItem(nVals[0]));
334 if(nVals[1] != nVal2)
336 GetProperties().SetObjectItemDirect(SdrEdgeLine2DeltaItem(nVals[1]));
339 if(nVals[2] != nVal3)
341 GetProperties().SetObjectItemDirect(SdrEdgeLine3DeltaItem(nVals[2]));
344 if(n < 3)
346 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE3DELTA);
349 if(n < 2)
351 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE2DELTA);
354 if(n < 1)
356 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE1DELTA);
361 void SdrEdgeObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
363 rInfo.bRotateFreeAllowed=sal_False;
364 rInfo.bRotate90Allowed =sal_False;
365 rInfo.bMirrorFreeAllowed=sal_False;
366 rInfo.bMirror45Allowed =sal_False;
367 rInfo.bMirror90Allowed =sal_False;
368 rInfo.bTransparenceAllowed = sal_False;
369 rInfo.bGradientAllowed = sal_False;
370 rInfo.bShearAllowed =sal_False;
371 rInfo.bEdgeRadiusAllowed=sal_False;
372 bool bCanConv=!HasText() || ImpCanConvTextToCurve();
373 rInfo.bCanConvToPath=bCanConv;
374 rInfo.bCanConvToPoly=bCanConv;
375 rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
378 sal_uInt16 SdrEdgeObj::GetObjIdentifier() const
380 return sal_uInt16(OBJ_EDGE);
383 const Rectangle& SdrEdgeObj::GetCurrentBoundRect() const
385 if(bEdgeTrackDirty)
387 ((SdrEdgeObj*)this)->ImpRecalcEdgeTrack();
390 return SdrTextObj::GetCurrentBoundRect();
393 const Rectangle& SdrEdgeObj::GetSnapRect() const
395 if(bEdgeTrackDirty)
397 ((SdrEdgeObj*)this)->ImpRecalcEdgeTrack();
400 return SdrTextObj::GetSnapRect();
403 void SdrEdgeObj::RecalcSnapRect()
405 maSnapRect=pEdgeTrack->GetBoundRect();
408 void SdrEdgeObj::TakeUnrotatedSnapRect(Rectangle& rRect) const
410 rRect=GetSnapRect();
413 bool SdrEdgeObj::IsNode() const
415 return true;
418 SdrGluePoint SdrEdgeObj::GetVertexGluePoint(sal_uInt16 nNum) const
420 Point aPt;
421 sal_uInt16 nPntAnz=pEdgeTrack->GetPointCount();
422 if (nPntAnz>0)
424 Point aOfs = GetSnapRect().Center();
425 if (nNum==2 && GetConnectedNode(sal_True)==NULL) aPt=(*pEdgeTrack)[0];
426 else if (nNum==3 && GetConnectedNode(sal_False)==NULL) aPt=(*pEdgeTrack)[nPntAnz-1];
427 else {
428 if ((nPntAnz & 1) ==1) {
429 aPt=(*pEdgeTrack)[nPntAnz/2];
430 } else {
431 Point aPt1((*pEdgeTrack)[nPntAnz/2-1]);
432 Point aPt2((*pEdgeTrack)[nPntAnz/2]);
433 aPt1+=aPt2;
434 aPt1.X()/=2;
435 aPt1.Y()/=2;
436 aPt=aPt1;
439 aPt-=aOfs;
441 SdrGluePoint aGP(aPt);
442 aGP.SetPercent(sal_False);
443 return aGP;
446 SdrGluePoint SdrEdgeObj::GetCornerGluePoint(sal_uInt16 nNum) const
448 return GetVertexGluePoint(nNum);
451 const SdrGluePointList* SdrEdgeObj::GetGluePointList() const
453 return NULL; // no user defined glue points for connectors
456 SdrGluePointList* SdrEdgeObj::ForceGluePointList()
458 return NULL; // no user defined glue points for connectors
461 bool SdrEdgeObj::IsEdge() const
463 return true;
466 void SdrEdgeObj::ConnectToNode(bool bTail1, SdrObject* pObj)
468 SdrObjConnection& rCon=GetConnection(bTail1);
469 DisconnectFromNode(bTail1);
470 if (pObj!=NULL) {
471 pObj->AddListener(*this);
472 rCon.pObj=pObj;
473 ImpDirtyEdgeTrack();
477 void SdrEdgeObj::DisconnectFromNode(bool bTail1)
479 SdrObjConnection& rCon=GetConnection(bTail1);
480 if (rCon.pObj!=NULL) {
481 rCon.pObj->RemoveListener(*this);
482 rCon.pObj=NULL;
486 SdrObject* SdrEdgeObj::GetConnectedNode(bool bTail1) const
488 SdrObject* pObj=GetConnection(bTail1).pObj;
489 if (pObj!=NULL && (pObj->GetPage()!=pPage || !pObj->IsInserted())) pObj=NULL;
490 return pObj;
493 bool SdrEdgeObj::CheckNodeConnection(bool bTail1) const
495 bool bRet = false;
496 const SdrObjConnection& rCon=GetConnection(bTail1);
497 sal_uInt16 nPtAnz=pEdgeTrack->GetPointCount();
498 if (rCon.pObj!=NULL && rCon.pObj->GetPage()==pPage && nPtAnz!=0) {
499 const SdrGluePointList* pGPL=rCon.pObj->GetGluePointList();
500 sal_uInt16 nConAnz=pGPL==NULL ? 0 : pGPL->GetCount();
501 sal_uInt16 nGesAnz=nConAnz+8;
502 Point aTail(bTail1 ? (*pEdgeTrack)[0] : (*pEdgeTrack)[sal_uInt16(nPtAnz-1)]);
503 for (sal_uInt16 i=0; i<nGesAnz && !bRet; i++) {
504 if (i<nConAnz) { // UserDefined
505 bRet=aTail==(*pGPL)[i].GetAbsolutePos(*rCon.pObj);
506 } else if (i<nConAnz+4) { // Vertex
507 SdrGluePoint aPt(rCon.pObj->GetVertexGluePoint(i-nConAnz));
508 bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
509 } else { // Corner
510 SdrGluePoint aPt(rCon.pObj->GetCornerGluePoint(i-nConAnz-4));
511 bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
515 return bRet;
518 void SdrEdgeObj::ImpSetTailPoint(bool bTail1, const Point& rPt)
520 sal_uInt16 nPtAnz=pEdgeTrack->GetPointCount();
521 if (nPtAnz==0) {
522 (*pEdgeTrack)[0]=rPt;
523 (*pEdgeTrack)[1]=rPt;
524 } else if (nPtAnz==1) {
525 if (!bTail1) (*pEdgeTrack)[1]=rPt;
526 else { (*pEdgeTrack)[1]=(*pEdgeTrack)[0]; (*pEdgeTrack)[0]=rPt; }
527 } else {
528 if (!bTail1) (*pEdgeTrack)[sal_uInt16(nPtAnz-1)]=rPt;
529 else (*pEdgeTrack)[0]=rPt;
531 ImpRecalcEdgeTrack();
532 SetRectsDirty();
535 void SdrEdgeObj::ImpDirtyEdgeTrack()
537 if ( !bEdgeTrackUserDefined || !(GetModel() && GetModel()->isLocked()) )
538 bEdgeTrackDirty = sal_True;
541 void SdrEdgeObj::ImpUndirtyEdgeTrack()
543 if (bEdgeTrackDirty && (GetModel() && GetModel()->isLocked()) ) {
544 ImpRecalcEdgeTrack();
548 void SdrEdgeObj::ImpRecalcEdgeTrack()
550 if ( bEdgeTrackUserDefined && (GetModel() && GetModel()->isLocked()) )
551 return;
553 if(IsBoundRectCalculationRunning())
555 // This object is involved into another ImpRecalcEdgeTrack() call
556 // from another SdrEdgeObj. Do not calculate again to avoid loop.
557 // Also, do not change bEdgeTrackDirty so that it gets recalculated
558 // later at the first non-looping call.
560 // #i43068#
561 else if(GetModel() && GetModel()->isLocked())
563 // avoid re-layout during imports/API call sequences
564 // #i45294# but calculate EdgeTrack and secure properties there
565 ((SdrEdgeObj*)this)->mbBoundRectCalculationRunning = sal_True;
566 *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
567 ImpSetAttrToEdgeInfo();
568 bEdgeTrackDirty=sal_False;
569 ((SdrEdgeObj*)this)->mbBoundRectCalculationRunning = sal_False;
571 else
573 // To not run in a depth loop, use a coloring algorithm on
574 // SdrEdgeObj BoundRect calculations
575 ((SdrEdgeObj*)this)->mbBoundRectCalculationRunning = sal_True;
577 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect();
578 SetRectsDirty();
579 *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
580 ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
581 bEdgeTrackDirty=sal_False;
583 // Only redraw here, no object change
584 ActionChanged();
586 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0);
588 ((SdrEdgeObj*)this)->mbBoundRectCalculationRunning = sal_False;
592 sal_uInt16 SdrEdgeObj::ImpCalcEscAngle(SdrObject* pObj, const Point& rPt) const
594 if (pObj==NULL) return SDRESC_ALL;
595 Rectangle aR(pObj->GetSnapRect());
596 long dxl=rPt.X()-aR.Left();
597 long dyo=rPt.Y()-aR.Top();
598 long dxr=aR.Right()-rPt.X();
599 long dyu=aR.Bottom()-rPt.Y();
600 bool bxMitt=Abs(dxl-dxr)<2;
601 bool byMitt=Abs(dyo-dyu)<2;
602 long dx=Min(dxl,dxr);
603 long dy=Min(dyo,dyu);
604 bool bDiag=Abs(dx-dy)<2;
605 if (bxMitt && byMitt) return SDRESC_ALL; // in the center
606 if (bDiag) { // diagonally
607 sal_uInt16 nRet=0;
608 if (byMitt) nRet|=SDRESC_VERT;
609 if (bxMitt) nRet|=SDRESC_HORZ;
610 if (dxl<dxr) { // left
611 if (dyo<dyu) nRet|=SDRESC_LEFT | SDRESC_TOP;
612 else nRet|=SDRESC_LEFT | SDRESC_BOTTOM;
613 } else { // right
614 if (dyo<dyu) nRet|=SDRESC_RIGHT | SDRESC_TOP;
615 else nRet|=SDRESC_RIGHT | SDRESC_BOTTOM;
617 return nRet;
619 if (dx<dy) { // horizontal
620 if (bxMitt) return SDRESC_HORZ;
621 if (dxl<dxr) return SDRESC_LEFT;
622 else return SDRESC_RIGHT;
623 } else { // vertical
624 if (byMitt) return SDRESC_VERT;
625 if (dyo<dyu) return SDRESC_TOP;
626 else return SDRESC_BOTTOM;
630 XPolygon SdrEdgeObj::ImpCalcObjToCenter(const Point& rStPt, long nEscAngle, const Rectangle& rRect, const Point& rMeeting) const
632 XPolygon aXP;
633 aXP.Insert(XPOLY_APPEND,rStPt,XPOLY_NORMAL);
634 bool bRts=nEscAngle==0;
635 bool bObn=nEscAngle==9000;
636 bool bLks=nEscAngle==18000;
637 bool bUnt=nEscAngle==27000;
639 Point aP1(rStPt); // mandatory difference first,...
640 if (bLks) aP1.X()=rRect.Left();
641 if (bRts) aP1.X()=rRect.Right();
642 if (bObn) aP1.Y()=rRect.Top();
643 if (bUnt) aP1.Y()=rRect.Bottom();
645 Point aP2(aP1); // ...now increase to Meeting height, if necessary
646 if (bLks && rMeeting.X()<=aP2.X()) aP2.X()=rMeeting.X();
647 if (bRts && rMeeting.X()>=aP2.X()) aP2.X()=rMeeting.X();
648 if (bObn && rMeeting.Y()<=aP2.Y()) aP2.Y()=rMeeting.Y();
649 if (bUnt && rMeeting.Y()>=aP2.Y()) aP2.Y()=rMeeting.Y();
650 aXP.Insert(XPOLY_APPEND,aP2,XPOLY_NORMAL);
652 Point aP3(aP2);
653 if ((bLks && rMeeting.X()>aP2.X()) || (bRts && rMeeting.X()<aP2.X())) { // around
654 if (rMeeting.Y()<aP2.Y()) {
655 aP3.Y()=rRect.Top();
656 if (rMeeting.Y()<aP3.Y()) aP3.Y()=rMeeting.Y();
657 } else {
658 aP3.Y()=rRect.Bottom();
659 if (rMeeting.Y()>aP3.Y()) aP3.Y()=rMeeting.Y();
661 aXP.Insert(XPOLY_APPEND,aP3,XPOLY_NORMAL);
662 if (aP3.Y()!=rMeeting.Y()) {
663 aP3.X()=rMeeting.X();
664 aXP.Insert(XPOLY_APPEND,aP3,XPOLY_NORMAL);
667 if ((bObn && rMeeting.Y()>aP2.Y()) || (bUnt && rMeeting.Y()<aP2.Y())) { // around
668 if (rMeeting.X()<aP2.X()) {
669 aP3.X()=rRect.Left();
670 if (rMeeting.X()<aP3.X()) aP3.X()=rMeeting.X();
671 } else {
672 aP3.X()=rRect.Right();
673 if (rMeeting.X()>aP3.X()) aP3.X()=rMeeting.X();
675 aXP.Insert(XPOLY_APPEND,aP3,XPOLY_NORMAL);
676 if (aP3.X()!=rMeeting.X()) {
677 aP3.Y()=rMeeting.Y();
678 aXP.Insert(XPOLY_APPEND,aP3,XPOLY_NORMAL);
681 #ifdef DBG_UTIL
682 if (aXP.GetPointCount()>4) {
683 OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
685 #endif
686 return aXP;
689 XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const XPolygon& rTrack0, SdrObjConnection& rCon1, SdrObjConnection& rCon2, SdrEdgeInfoRec* pInfo) const
691 Point aPt1,aPt2;
692 SdrGluePoint aGP1,aGP2;
693 sal_uInt16 nEsc1=SDRESC_ALL,nEsc2=SDRESC_ALL;
694 Rectangle aBoundRect1;
695 Rectangle aBoundRect2;
696 Rectangle aBewareRect1;
697 Rectangle aBewareRect2;
698 // first, get the old corner points
699 if (rTrack0.GetPointCount()!=0) {
700 aPt1=rTrack0[0];
701 sal_uInt16 nSiz=rTrack0.GetPointCount();
702 nSiz--;
703 aPt2=rTrack0[nSiz];
704 } else {
705 if (!aOutRect.IsEmpty()) {
706 aPt1=aOutRect.TopLeft();
707 aPt2=aOutRect.BottomRight();
710 bool bCon1=rCon1.pObj!=NULL && rCon1.pObj->GetPage()==pPage && rCon1.pObj->IsInserted();
711 bool bCon2=rCon2.pObj!=NULL && rCon2.pObj->GetPage()==pPage && rCon2.pObj->IsInserted();
712 const SfxItemSet& rSet = GetObjectItemSet();
714 if (bCon1) {
715 if (rCon1.pObj==(SdrObject*)this)
717 // check, just in case
718 aBoundRect1=aOutRect;
720 else
722 aBoundRect1 = rCon1.pObj->GetCurrentBoundRect();
724 aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
725 aBewareRect1=aBoundRect1;
727 sal_Int32 nH = ((SdrEdgeNode1HorzDistItem&)rSet.Get(SDRATTR_EDGENODE1HORZDIST)).GetValue();
728 sal_Int32 nV = ((SdrEdgeNode1VertDistItem&)rSet.Get(SDRATTR_EDGENODE1VERTDIST)).GetValue();
730 aBewareRect1.Left()-=nH;
731 aBewareRect1.Right()+=nH;
732 aBewareRect1.Top()-=nV;
733 aBewareRect1.Bottom()+=nV;
734 } else {
735 aBoundRect1=Rectangle(aPt1,aPt1);
736 aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
737 aBewareRect1=aBoundRect1;
739 if (bCon2) {
740 if (rCon2.pObj==(SdrObject*)this) { // check, just in case
741 aBoundRect2=aOutRect;
743 else
745 aBoundRect2 = rCon2.pObj->GetCurrentBoundRect();
747 aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
748 aBewareRect2=aBoundRect2;
750 sal_Int32 nH = ((SdrEdgeNode2HorzDistItem&)rSet.Get(SDRATTR_EDGENODE2HORZDIST)).GetValue();
751 sal_Int32 nV = ((SdrEdgeNode2VertDistItem&)rSet.Get(SDRATTR_EDGENODE2VERTDIST)).GetValue();
753 aBewareRect2.Left()-=nH;
754 aBewareRect2.Right()+=nH;
755 aBewareRect2.Top()-=nV;
756 aBewareRect2.Bottom()+=nV;
757 } else {
758 aBoundRect2=Rectangle(aPt2,aPt2);
759 aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
760 aBewareRect2=aBoundRect2;
762 XPolygon aBestXP;
763 sal_uIntPtr nBestQual=0xFFFFFFFF;
764 SdrEdgeInfoRec aBestInfo;
765 bool bAuto1=bCon1 && rCon1.bBestVertex;
766 bool bAuto2=bCon2 && rCon2.bBestVertex;
767 if (bAuto1) rCon1.bAutoVertex=sal_True;
768 if (bAuto2) rCon2.bAutoVertex=sal_True;
769 sal_uInt16 nBestAuto1=0;
770 sal_uInt16 nBestAuto2=0;
771 sal_uInt16 nAnz1=bAuto1 ? 4 : 1;
772 sal_uInt16 nAnz2=bAuto2 ? 4 : 1;
773 for (sal_uInt16 nNum1=0; nNum1<nAnz1; nNum1++) {
774 if (bAuto1) rCon1.nConId=nNum1;
775 if (bCon1 && rCon1.TakeGluePoint(aGP1,sal_True)) {
776 aPt1=aGP1.GetPos();
777 nEsc1=aGP1.GetEscDir();
778 if (nEsc1==SDRESC_SMART) nEsc1=ImpCalcEscAngle(rCon1.pObj,aPt1-rCon1.aObjOfs);
780 for (sal_uInt16 nNum2=0; nNum2<nAnz2; nNum2++) {
781 if (bAuto2) rCon2.nConId=nNum2;
782 if (bCon2 && rCon2.TakeGluePoint(aGP2,sal_True)) {
783 aPt2=aGP2.GetPos();
784 nEsc2=aGP2.GetEscDir();
785 if (nEsc2==SDRESC_SMART) nEsc2=ImpCalcEscAngle(rCon2.pObj,aPt2-rCon2.aObjOfs);
787 for (long nA1=0; nA1<36000; nA1+=9000) {
788 sal_uInt16 nE1=nA1==0 ? SDRESC_RIGHT : nA1==9000 ? SDRESC_TOP : nA1==18000 ? SDRESC_LEFT : nA1==27000 ? SDRESC_BOTTOM : 0;
789 for (long nA2=0; nA2<36000; nA2+=9000) {
790 sal_uInt16 nE2=nA2==0 ? SDRESC_RIGHT : nA2==9000 ? SDRESC_TOP : nA2==18000 ? SDRESC_LEFT : nA2==27000 ? SDRESC_BOTTOM : 0;
791 if ((nEsc1&nE1)!=0 && (nEsc2&nE2)!=0) {
792 sal_uIntPtr nQual=0;
793 SdrEdgeInfoRec aInfo;
794 if (pInfo!=NULL) aInfo=*pInfo;
795 XPolygon aXP(ImpCalcEdgeTrack(aPt1,nA1,aBoundRect1,aBewareRect1,aPt2,nA2,aBoundRect2,aBewareRect2,&nQual,&aInfo));
796 if (nQual<nBestQual) {
797 aBestXP=aXP;
798 nBestQual=nQual;
799 aBestInfo=aInfo;
800 nBestAuto1=nNum1;
801 nBestAuto2=nNum2;
808 if (bAuto1) rCon1.nConId=nBestAuto1;
809 if (bAuto2) rCon2.nConId=nBestAuto2;
810 if (pInfo!=NULL) *pInfo=aBestInfo;
811 return aBestXP;
814 XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const Point& rPt1, long nAngle1, const Rectangle& rBoundRect1, const Rectangle& rBewareRect1,
815 const Point& rPt2, long nAngle2, const Rectangle& rBoundRect2, const Rectangle& rBewareRect2,
816 sal_uIntPtr* pnQuality, SdrEdgeInfoRec* pInfo) const
818 SdrEdgeKind eKind=((SdrEdgeKindItem&)(GetObjectItem(SDRATTR_EDGEKIND))).GetValue();
819 bool bRts1=nAngle1==0;
820 bool bObn1=nAngle1==9000;
821 bool bLks1=nAngle1==18000;
822 bool bUnt1=nAngle1==27000;
823 bool bHor1=bLks1 || bRts1;
824 bool bVer1=bObn1 || bUnt1;
825 bool bRts2=nAngle2==0;
826 bool bObn2=nAngle2==9000;
827 bool bLks2=nAngle2==18000;
828 bool bUnt2=nAngle2==27000;
829 bool bHor2=bLks2 || bRts2;
830 bool bVer2=bObn2 || bUnt2;
831 bool bInfo=pInfo!=NULL;
832 if (bInfo) {
833 pInfo->cOrthoForm=0;
834 pInfo->nAngle1=nAngle1;
835 pInfo->nAngle2=nAngle2;
836 pInfo->nObj1Lines=1;
837 pInfo->nObj2Lines=1;
838 pInfo->nMiddleLine=0xFFFF;
840 Point aPt1(rPt1);
841 Point aPt2(rPt2);
842 Rectangle aBoundRect1 (rBoundRect1 );
843 Rectangle aBoundRect2 (rBoundRect2 );
844 Rectangle aBewareRect1(rBewareRect1);
845 Rectangle aBewareRect2(rBewareRect2);
846 Point aMeeting((aPt1.X()+aPt2.X()+1)/2,(aPt1.Y()+aPt2.Y()+1)/2);
847 if (eKind==SDREDGE_ONELINE) {
848 XPolygon aXP(2);
849 aXP[0]=rPt1;
850 aXP[1]=rPt2;
851 if (pnQuality!=NULL) {
852 *pnQuality=Abs(rPt1.X()-rPt2.X())+Abs(rPt1.Y()-rPt2.Y());
854 return aXP;
855 } else if (eKind==SDREDGE_THREELINES) {
856 XPolygon aXP(4);
857 aXP[0]=rPt1;
858 aXP[1]=rPt1;
859 aXP[2]=rPt2;
860 aXP[3]=rPt2;
861 if (bRts1) aXP[1].X()=aBewareRect1.Right(); //+=500;
862 if (bObn1) aXP[1].Y()=aBewareRect1.Top(); //-=500;
863 if (bLks1) aXP[1].X()=aBewareRect1.Left(); //-=500;
864 if (bUnt1) aXP[1].Y()=aBewareRect1.Bottom(); //+=500;
865 if (bRts2) aXP[2].X()=aBewareRect2.Right(); //+=500;
866 if (bObn2) aXP[2].Y()=aBewareRect2.Top(); //-=500;
867 if (bLks2) aXP[2].X()=aBewareRect2.Left(); //-=500;
868 if (bUnt2) aXP[2].Y()=aBewareRect2.Bottom(); //+=500;
869 if (pnQuality!=NULL) {
870 long nQ=Abs(aXP[1].X()-aXP[0].X())+Abs(aXP[1].Y()-aXP[0].Y());
871 nQ+=Abs(aXP[2].X()-aXP[1].X())+Abs(aXP[2].Y()-aXP[1].Y());
872 nQ+=Abs(aXP[3].X()-aXP[2].X())+Abs(aXP[3].Y()-aXP[2].Y());
873 *pnQuality=nQ;
875 if (bInfo) {
876 pInfo->nObj1Lines=2;
877 pInfo->nObj2Lines=2;
878 if (bHor1) {
879 aXP[1].X()+=pInfo->aObj1Line2.X();
880 } else {
881 aXP[1].Y()+=pInfo->aObj1Line2.Y();
883 if (bHor2) {
884 aXP[2].X()+=pInfo->aObj2Line2.X();
885 } else {
886 aXP[2].Y()+=pInfo->aObj2Line2.Y();
889 return aXP;
891 sal_uInt16 nIntersections=0;
893 Point aC1(aBewareRect1.Center());
894 Point aC2(aBewareRect2.Center());
895 if (aBewareRect1.Left()<=aBewareRect2.Right() && aBewareRect1.Right()>=aBewareRect2.Left()) {
896 // overlapping on the x axis
897 long n1=Max(aBewareRect1.Left(),aBewareRect2.Left());
898 long n2=Min(aBewareRect1.Right(),aBewareRect2.Right());
899 aMeeting.X()=(n1+n2+1)/2;
900 } else {
901 // otherwise the center point of the empty space
902 if (aC1.X()<aC2.X()) {
903 aMeeting.X()=(aBewareRect1.Right()+aBewareRect2.Left()+1)/2;
904 } else {
905 aMeeting.X()=(aBewareRect1.Left()+aBewareRect2.Right()+1)/2;
908 if (aBewareRect1.Top()<=aBewareRect2.Bottom() && aBewareRect1.Bottom()>=aBewareRect2.Top()) {
909 // overlapping on the x axis
910 long n1=Max(aBewareRect1.Top(),aBewareRect2.Top());
911 long n2=Min(aBewareRect1.Bottom(),aBewareRect2.Bottom());
912 aMeeting.Y()=(n1+n2+1)/2;
913 } else {
914 // otherwise the center point of the empty space
915 if (aC1.Y()<aC2.Y()) {
916 aMeeting.Y()=(aBewareRect1.Bottom()+aBewareRect2.Top()+1)/2;
917 } else {
918 aMeeting.Y()=(aBewareRect1.Top()+aBewareRect2.Bottom()+1)/2;
921 // Here, there are three cases:
922 // 1. both go into the same direction
923 // 2. both go into opposite directions
924 // 3. one is vertical, the other is horizontal
925 long nXMin=Min(aBewareRect1.Left(),aBewareRect2.Left());
926 long nXMax=Max(aBewareRect1.Right(),aBewareRect2.Right());
927 long nYMin=Min(aBewareRect1.Top(),aBewareRect2.Top());
928 long nYMax=Max(aBewareRect1.Bottom(),aBewareRect2.Bottom());
929 bool bBewareOverlap=aBewareRect1.Right()>aBewareRect2.Left() && aBewareRect1.Left()<aBewareRect2.Right() &&
930 aBewareRect1.Bottom()>aBewareRect2.Top() && aBewareRect1.Top()<aBewareRect2.Bottom();
931 unsigned nMainCase=3;
932 if (nAngle1==nAngle2) nMainCase=1;
933 else if ((bHor1 && bHor2) || (bVer1 && bVer2)) nMainCase=2;
934 if (nMainCase==1) { // case 1 (both go in one direction) is possible
935 if (bVer1) aMeeting.X()=(aPt1.X()+aPt2.X()+1)/2; // Here, this is better than
936 if (bHor1) aMeeting.Y()=(aPt1.Y()+aPt2.Y()+1)/2; // using center point of empty space
937 // bX1Ok means that the vertical exiting Obj1 doesn't conflict with Obj2, ...
938 bool bX1Ok=aPt1.X()<=aBewareRect2.Left() || aPt1.X()>=aBewareRect2.Right();
939 bool bX2Ok=aPt2.X()<=aBewareRect1.Left() || aPt2.X()>=aBewareRect1.Right();
940 bool bY1Ok=aPt1.Y()<=aBewareRect2.Top() || aPt1.Y()>=aBewareRect2.Bottom();
941 bool bY2Ok=aPt2.Y()<=aBewareRect1.Top() || aPt2.Y()>=aBewareRect1.Bottom();
942 if (bLks1 && (bY1Ok || aBewareRect1.Left()<aBewareRect2.Right()) && (bY2Ok || aBewareRect2.Left()<aBewareRect1.Right())) {
943 aMeeting.X()=nXMin;
945 if (bRts1 && (bY1Ok || aBewareRect1.Right()>aBewareRect2.Left()) && (bY2Ok || aBewareRect2.Right()>aBewareRect1.Left())) {
946 aMeeting.X()=nXMax;
948 if (bObn1 && (bX1Ok || aBewareRect1.Top()<aBewareRect2.Bottom()) && (bX2Ok || aBewareRect2.Top()<aBewareRect1.Bottom())) {
949 aMeeting.Y()=nYMin;
951 if (bUnt1 && (bX1Ok || aBewareRect1.Bottom()>aBewareRect2.Top()) && (bX2Ok || aBewareRect2.Bottom()>aBewareRect1.Top())) {
952 aMeeting.Y()=nYMax;
954 } else if (nMainCase==2) {
955 // case 2:
956 if (bHor1) { // both horizontal
957 /* 9 sub-cases:
958 (legend: line exits to the left (-|), right (|-))
960 2.1: Facing; overlap only on y axis
961 * * *
962 |--| *
963 * * *
965 2.2, 2.3: Facing, offset vertically; no overlap on either
966 axis
967 |- * * * * *
968 * -| * * -| *
969 * * * , * * *
971 2.4, 2.5: One below the other; overlap only on y axis
972 * |- * * * *
973 * -| * * -| *
974 * * * , * |- *
976 2.6, 2.7: Not facing, offset vertically; no overlap on either
977 axis
978 * * |- * * *
979 * -| * * -| *
980 * * * , * * |-
982 2.8: Not facing; overlap only on y axis
983 * * *
984 * -| |-
985 * * *
987 2.9: The objects's BewareRects overlap on x and y axis
989 These cases, with some modifications are also valid for
990 horizontal line exits.
991 Cases 2.1 through 2.7 are covered well enough with the
992 default meetings. Only for cases 2.8 and 2.9 do we determine
993 special meeting points here.
996 // normalization; be aR1 the one exiting to the right,
997 // be aR2 the one exiting to the left
998 Rectangle aBewR1(bRts1 ? aBewareRect1 : aBewareRect2);
999 Rectangle aBewR2(bRts1 ? aBewareRect2 : aBewareRect1);
1000 Rectangle aBndR1(bRts1 ? aBoundRect1 : aBoundRect2);
1001 Rectangle aBndR2(bRts1 ? aBoundRect2 : aBoundRect1);
1002 if (aBewR1.Bottom()>aBewR2.Top() && aBewR1.Top()<aBewR2.Bottom()) {
1003 // overlap on y axis; cases 2.1, 2.8, 2.9
1004 if (aBewR1.Right()>aBewR2.Left()) {
1005 /* Cases 2.8, 2.9:
1006 Case 2.8: always going around on the outside
1007 (bDirect=sal_False).
1009 Case 2.9 could also be a direct connection (in the
1010 case that the BewareRects overlap only slightly and
1011 the BoundRects don't overlap at all and if the
1012 line exits would otherwise violate the respective
1013 other object's BewareRect).
1015 bool bCase29Direct = false;
1016 bool bCase29=aBewR1.Right()>aBewR2.Left();
1017 if (aBndR1.Right()<=aBndR2.Left()) { // case 2.9 without BoundRect overlap
1018 if ((aPt1.Y()>aBewareRect2.Top() && aPt1.Y()<aBewareRect2.Bottom()) ||
1019 (aPt2.Y()>aBewareRect1.Top() && aPt2.Y()<aBewareRect1.Bottom())) {
1020 bCase29Direct = true;
1023 if (!bCase29Direct) {
1024 bool bObenLang=Abs(nYMin-aMeeting.Y())<=Abs(nYMax-aMeeting.Y());
1025 if (bObenLang) {
1026 aMeeting.Y()=nYMin;
1027 } else {
1028 aMeeting.Y()=nYMax;
1030 if (bCase29) {
1031 // now make sure that the surrounded object
1032 // isn't traversed
1033 if ((aBewR1.Center().Y()<aBewR2.Center().Y()) != bObenLang) {
1034 aMeeting.X()=aBewR2.Right();
1035 } else {
1036 aMeeting.X()=aBewR1.Left();
1039 } else {
1040 // We need a direct connection (3-line Z connection),
1041 // because we have to violate the BewareRects.
1042 // Use rule of three to scale down the BewareRects.
1043 long nWant1=aBewR1.Right()-aBndR1.Right(); // distance at Obj1
1044 long nWant2=aBndR2.Left()-aBewR2.Left(); // distance at Obj2
1045 long nSpace=aBndR2.Left()-aBndR1.Right(); // available space
1046 long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
1047 long nGet2=nSpace-nGet1;
1048 if (bRts1) { // revert normalization
1049 aBewareRect1.Right()+=nGet1-nWant1;
1050 aBewareRect2.Left()-=nGet2-nWant2;
1051 } else {
1052 aBewareRect2.Right()+=nGet1-nWant1;
1053 aBewareRect1.Left()-=nGet2-nWant2;
1055 nIntersections++; // lower quality
1059 } else if (bVer1) { // both horizontal
1060 Rectangle aBewR1(bUnt1 ? aBewareRect1 : aBewareRect2);
1061 Rectangle aBewR2(bUnt1 ? aBewareRect2 : aBewareRect1);
1062 Rectangle aBndR1(bUnt1 ? aBoundRect1 : aBoundRect2);
1063 Rectangle aBndR2(bUnt1 ? aBoundRect2 : aBoundRect1);
1064 if (aBewR1.Right()>aBewR2.Left() && aBewR1.Left()<aBewR2.Right()) {
1065 // overlap on y axis; cases 2.1, 2.8, 2.9
1066 if (aBewR1.Bottom()>aBewR2.Top()) {
1067 /* Cases 2.8, 2.9
1068 Case 2.8 always going around on the outside (bDirect=sal_False).
1070 Case 2.9 could also be a direct connection (in the
1071 case that the BewareRects overlap only slightly and
1072 the BoundRects don't overlap at all and if the
1073 line exits would otherwise violate the respective
1074 other object's BewareRect).
1076 bool bCase29Direct = false;
1077 bool bCase29=aBewR1.Bottom()>aBewR2.Top();
1078 if (aBndR1.Bottom()<=aBndR2.Top()) { // case 2.9 without BoundRect overlap
1079 if ((aPt1.X()>aBewareRect2.Left() && aPt1.X()<aBewareRect2.Right()) ||
1080 (aPt2.X()>aBewareRect1.Left() && aPt2.X()<aBewareRect1.Right())) {
1081 bCase29Direct = true;
1084 if (!bCase29Direct) {
1085 bool bLinksLang=Abs(nXMin-aMeeting.X())<=Abs(nXMax-aMeeting.X());
1086 if (bLinksLang) {
1087 aMeeting.X()=nXMin;
1088 } else {
1089 aMeeting.X()=nXMax;
1091 if (bCase29) {
1092 // now make sure that the surrounded object
1093 // isn't traversed
1094 if ((aBewR1.Center().X()<aBewR2.Center().X()) != bLinksLang) {
1095 aMeeting.Y()=aBewR2.Bottom();
1096 } else {
1097 aMeeting.Y()=aBewR1.Top();
1100 } else {
1101 // We need a direct connection (3-line Z connection),
1102 // because we have to violate the BewareRects.
1103 // Use rule of three to scale down the BewareRects.
1104 long nWant1=aBewR1.Bottom()-aBndR1.Bottom(); // difference at Obj1
1105 long nWant2=aBndR2.Top()-aBewR2.Top(); // difference at Obj2
1106 long nSpace=aBndR2.Top()-aBndR1.Bottom(); // available space
1107 long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
1108 long nGet2=nSpace-nGet1;
1109 if (bUnt1) { // revert normalization
1110 aBewareRect1.Bottom()+=nGet1-nWant1;
1111 aBewareRect2.Top()-=nGet2-nWant2;
1112 } else {
1113 aBewareRect2.Bottom()+=nGet1-nWant1;
1114 aBewareRect1.Top()-=nGet2-nWant2;
1116 nIntersections++; // lower quality
1121 } else if (nMainCase==3) { // case 3: one horizontal, the other vertical
1122 /* legend:
1123 The line exits to the:
1124 -| left
1126 |- right
1128 _|_ top
1130 T bottom
1132 * . * . * -- no overlap, at most might touch
1133 . . . . . -- overlap
1134 * . |- . * -- same height
1135 . . . . . -- overlap
1136 * . * . * -- no overlap, at most might touch
1138 Overall, there are 96 possible constellations, some of these can't even
1139 be unambiguously assigned to a certain case/method of handling.
1142 3.1: All those constellations that are covered reasonably well
1143 by the default MeetingPoint (20+12).
1145 T T T . _|_ _|_ . T T T these 12 * . * T * * . * . * * T * . * * . * . *
1146 . . . . _|_ _|_ . . . . constellations . . . . . . . . . T . . . . . T . . . .
1147 * . |- . * * . -| . * are covered * . |- . _|_ * . |- . T _|_ . -| . * T . -| . *
1148 . . . . T T . . . . only in . . . . _|_ . . . . . _|_ . . . . . . . . .
1149 _|__|__|_ . T T . _|__|__|_ part: * . * _|_ * * . * . * * _|_ * . * * . * . *
1151 The last 16 of these cases can be excluded, if the objects face each other openly.
1154 3.2: The objects face each other openly, thus a connection using only two lines is possible (4+20);
1155 This case is priority #1.
1156 * . * . T T . * . * these 20 * . * T * * T * . * * . * . * * . * . *
1157 . . . . . . . . . . constellations . . . T T T T . . . . . . . . . . . . .
1158 * . |- . * * . -| . * are covered * . |-_|__|_ _|__|_-| . * * . |- T T T T -| . *
1159 . . . . . . . . . . only in . . . _|__|_ _|__|_ . . . . . . . . . . . . .
1160 * . * . _|_ _|_ . * . * part: * . * _|_ * * _|_ * . * * . * . * * . * . *
1162 3.3: The line exits point away from the other object or or miss its back (52+4).
1163 _|__|__|__|_ * * _|__|__|__|_ * . . . * * . * . * these 4 * . * . * * . * . *
1164 _|__|__|__|_ . . _|__|__|__|_ T T T . . . . T T T constellations . . . T . . T . . .
1165 _|__|_ |- . * * . -| _|__|_ T T |- . * * . -| T T are covered * . |- . * * . -| . *
1166 _|__|__|_ . . . . _|__|__|_ T T T T . . T T T T only in . . . _|_ . . _|_ . . .
1167 * . * . * * . * . * T T T T * * T T T T part: * . * . * * . * . *
1170 // case 3.2
1171 Rectangle aTmpR1(aBewareRect1);
1172 Rectangle aTmpR2(aBewareRect2);
1173 if (bBewareOverlap) {
1174 // overlapping BewareRects: use BoundRects for checking for case 3.2
1175 aTmpR1=aBoundRect1;
1176 aTmpR2=aBoundRect2;
1178 if ((((bRts1 && aTmpR1.Right ()<=aPt2.X()) || (bLks1 && aTmpR1.Left()>=aPt2.X())) &&
1179 ((bUnt2 && aTmpR2.Bottom()<=aPt1.Y()) || (bObn2 && aTmpR2.Top ()>=aPt1.Y()))) ||
1180 (((bRts2 && aTmpR2.Right ()<=aPt1.X()) || (bLks2 && aTmpR2.Left()>=aPt1.X())) &&
1181 ((bUnt1 && aTmpR1.Bottom()<=aPt2.Y()) || (bObn1 && aTmpR1.Top ()>=aPt2.Y())))) {
1182 // case 3.2 applies: connector with only 2 lines
1183 if (bHor1) {
1184 aMeeting.X()=aPt2.X();
1185 aMeeting.Y()=aPt1.Y();
1186 } else {
1187 aMeeting.X()=aPt1.X();
1188 aMeeting.Y()=aPt2.Y();
1190 // in the case of overlapping BewareRects:
1191 aBewareRect1=aTmpR1;
1192 aBewareRect2=aTmpR2;
1193 } else if ((((bRts1 && aBewareRect1.Right ()>aBewareRect2.Left ()) ||
1194 (bLks1 && aBewareRect1.Left ()<aBewareRect2.Right ())) &&
1195 ((bUnt2 && aBewareRect2.Bottom()>aBewareRect1.Top ()) ||
1196 (bObn2 && aBewareRect2.Top ()<aBewareRect1.Bottom()))) ||
1197 (((bRts2 && aBewareRect2.Right ()>aBewareRect1.Left ()) ||
1198 (bLks2 && aBewareRect2.Left ()<aBewareRect1.Right ())) &&
1199 ((bUnt1 && aBewareRect1.Bottom()>aBewareRect2.Top ()) ||
1200 (bObn1 && aBewareRect1.Top ()<aBewareRect2.Bottom())))) {
1201 // case 3.3
1202 if (bRts1 || bRts2) { aMeeting.X()=nXMax; }
1203 if (bLks1 || bLks2) { aMeeting.X()=nXMin; }
1204 if (bUnt1 || bUnt2) { aMeeting.Y()=nYMax; }
1205 if (bObn1 || bObn2) { aMeeting.Y()=nYMin; }
1210 XPolygon aXP1(ImpCalcObjToCenter(aPt1,nAngle1,aBewareRect1,aMeeting));
1211 XPolygon aXP2(ImpCalcObjToCenter(aPt2,nAngle2,aBewareRect2,aMeeting));
1212 sal_uInt16 nXP1Anz=aXP1.GetPointCount();
1213 sal_uInt16 nXP2Anz=aXP2.GetPointCount();
1214 if (bInfo) {
1215 pInfo->nObj1Lines=nXP1Anz; if (nXP1Anz>1) pInfo->nObj1Lines--;
1216 pInfo->nObj2Lines=nXP2Anz; if (nXP2Anz>1) pInfo->nObj2Lines--;
1218 Point aEP1(aXP1[nXP1Anz-1]);
1219 Point aEP2(aXP2[nXP2Anz-1]);
1220 bool bInsMeetingPoint=aEP1.X()!=aEP2.X() && aEP1.Y()!=aEP2.Y();
1221 bool bHorzE1=aEP1.Y()==aXP1[nXP1Anz-2].Y(); // is last line of XP1 horizontal?
1222 bool bHorzE2=aEP2.Y()==aXP2[nXP2Anz-2].Y(); // is last line of XP2 horizontal?
1223 if (aEP1==aEP2 && ((bHorzE1 && bHorzE2 && aEP1.Y()==aEP2.Y()) || (!bHorzE1 && !bHorzE2 && aEP1.X()==aEP2.X()))) {
1224 // special casing 'I' connectors
1225 nXP1Anz--; aXP1.Remove(nXP1Anz,1);
1226 nXP2Anz--; aXP2.Remove(nXP2Anz,1);
1228 if (bInsMeetingPoint) {
1229 aXP1.Insert(XPOLY_APPEND,aMeeting,XPOLY_NORMAL);
1230 if (bInfo) {
1231 // Inserting a MeetingPoint adds 2 new lines,
1232 // either might become the center line.
1233 if (pInfo->nObj1Lines==pInfo->nObj2Lines) {
1234 pInfo->nObj1Lines++;
1235 pInfo->nObj2Lines++;
1236 } else {
1237 if (pInfo->nObj1Lines>pInfo->nObj2Lines) {
1238 pInfo->nObj2Lines++;
1239 pInfo->nMiddleLine=nXP1Anz-1;
1240 } else {
1241 pInfo->nObj1Lines++;
1242 pInfo->nMiddleLine=nXP1Anz;
1246 } else if (bInfo && aEP1!=aEP2 && nXP1Anz+nXP2Anz>=4) {
1247 // By connecting both ends, another line is added, this becomes the center line.
1248 pInfo->nMiddleLine=nXP1Anz-1;
1250 sal_uInt16 nNum=aXP2.GetPointCount();
1251 if (aXP1[nXP1Anz-1]==aXP2[nXP2Anz-1] && nXP1Anz>1 && nXP2Anz>1) nNum--;
1252 while (nNum>0) {
1253 nNum--;
1254 aXP1.Insert(XPOLY_APPEND,aXP2[nNum],XPOLY_NORMAL);
1256 sal_uInt16 nPntAnz=aXP1.GetPointCount();
1257 char cForm=0;
1258 if (bInfo || pnQuality!=NULL) {
1259 cForm='?';
1260 if (nPntAnz==2) cForm='I';
1261 else if (nPntAnz==3) cForm='L';
1262 else if (nPntAnz==4) { // Z or U
1263 if (nAngle1==nAngle2) cForm='U';
1264 else cForm='Z';
1265 } else if (nPntAnz==6) { // S or C or ...
1266 if (nAngle1!=nAngle2) {
1267 // For type S, line 2 has the same direction as line 4.
1268 // For type C, the opposite is true.
1269 Point aP1(aXP1[1]);
1270 Point aP2(aXP1[2]);
1271 Point aP3(aXP1[3]);
1272 Point aP4(aXP1[4]);
1273 if (aP1.Y()==aP2.Y()) { // else both lines are horizontal
1274 if ((aP1.X()<aP2.X())==(aP3.X()<aP4.X())) cForm='S';
1275 else cForm='C';
1276 } else { // else both lines are vertical
1277 if ((aP1.Y()<aP2.Y())==(aP3.Y()<aP4.Y())) cForm='S';
1278 else cForm='C';
1280 } else cForm='4'; // else is case 3 with 5 lines
1281 } else cForm='?'; //
1282 // more shapes:
1283 if (bInfo) {
1284 pInfo->cOrthoForm=cForm;
1285 if (cForm=='I' || cForm=='L' || cForm=='Z' || cForm=='U') {
1286 pInfo->nObj1Lines=1;
1287 pInfo->nObj2Lines=1;
1288 if (cForm=='Z' || cForm=='U') {
1289 pInfo->nMiddleLine=1;
1290 } else {
1291 pInfo->nMiddleLine=0xFFFF;
1293 } else if (cForm=='S' || cForm=='C') {
1294 pInfo->nObj1Lines=2;
1295 pInfo->nObj2Lines=2;
1296 pInfo->nMiddleLine=2;
1300 if (pnQuality!=NULL) {
1301 sal_uIntPtr nQual=0;
1302 sal_uIntPtr nQual0=nQual; // prevent overruns
1303 bool bOverflow = false;
1304 Point aPt0(aXP1[0]);
1305 for (sal_uInt16 nPntNum=1; nPntNum<nPntAnz; nPntNum++) {
1306 Point aPt1b(aXP1[nPntNum]);
1307 nQual+=Abs(aPt1b.X()-aPt0.X())+Abs(aPt1b.Y()-aPt0.Y());
1308 if (nQual<nQual0) bOverflow = true;
1309 nQual0=nQual;
1310 aPt0=aPt1b;
1313 sal_uInt16 nTmp=nPntAnz;
1314 if (cForm=='Z') {
1315 nTmp=2; // Z shape with good quality (nTmp=2 instead of 4)
1316 sal_uIntPtr n1=Abs(aXP1[1].X()-aXP1[0].X())+Abs(aXP1[1].Y()-aXP1[0].Y());
1317 sal_uIntPtr n2=Abs(aXP1[2].X()-aXP1[1].X())+Abs(aXP1[2].Y()-aXP1[1].Y());
1318 sal_uIntPtr n3=Abs(aXP1[3].X()-aXP1[2].X())+Abs(aXP1[3].Y()-aXP1[2].Y());
1319 // try to make lines lengths similar
1320 sal_uIntPtr nBesser=0;
1321 n1+=n3;
1322 n3=n2/4;
1323 if (n1>=n2) nBesser=6;
1324 else if (n1>=3*n3) nBesser=4;
1325 else if (n1>=2*n3) nBesser=2;
1326 if (aXP1[0].Y()!=aXP1[1].Y()) nBesser++; // vertical starting line gets a plus (for H/V-Prio)
1327 if (nQual>nBesser) nQual-=nBesser; else nQual=0;
1329 if (nTmp>=3) {
1330 nQual0=nQual;
1331 nQual+=(sal_uIntPtr)nTmp*0x01000000;
1332 if (nQual<nQual0 || nTmp>15) bOverflow = true;
1334 if (nPntAnz>=2) { // check exit angle again
1335 Point aP1(aXP1[1]); aP1-=aXP1[0];
1336 Point aP2(aXP1[nPntAnz-2]); aP2-=aXP1[nPntAnz-1];
1337 long nAng1=0; if (aP1.X()<0) nAng1=18000; if (aP1.Y()>0) nAng1=27000;
1338 if (aP1.Y()<0) nAng1=9000; if (aP1.X()!=0 && aP1.Y()!=0) nAng1=1; // slant?!
1339 long nAng2=0; if (aP2.X()<0) nAng2=18000; if (aP2.Y()>0) nAng2=27000;
1340 if (aP2.Y()<0) nAng2=9000; if (aP2.X()!=0 && aP2.Y()!=0) nAng2=1; // slant?!
1341 if (nAng1!=nAngle1) nIntersections++;
1342 if (nAng2!=nAngle2) nIntersections++;
1345 // For the quality check, use the original Rects and at the same time
1346 // check whether one them was scaled down for the calculation of the
1347 // Edges (e. g. case 2.9)
1348 aBewareRect1=rBewareRect1;
1349 aBewareRect2=rBewareRect2;
1351 for (sal_uInt16 i=0; i<nPntAnz; i++) {
1352 Point aPt1b(aXP1[i]);
1353 bool b1=aPt1b.X()>aBewareRect1.Left() && aPt1b.X()<aBewareRect1.Right() &&
1354 aPt1b.Y()>aBewareRect1.Top() && aPt1b.Y()<aBewareRect1.Bottom();
1355 bool b2=aPt1b.X()>aBewareRect2.Left() && aPt1b.X()<aBewareRect2.Right() &&
1356 aPt1b.Y()>aBewareRect2.Top() && aPt1b.Y()<aBewareRect2.Bottom();
1357 sal_uInt16 nInt0=nIntersections;
1358 if (i==0 || i==nPntAnz-1) {
1359 if (b1 && b2) nIntersections++;
1360 } else {
1361 if (b1) nIntersections++;
1362 if (b2) nIntersections++;
1364 // check for overlaps
1365 if (i>0 && nInt0==nIntersections) {
1366 if (aPt0.Y()==aPt1b.Y()) { // horizontal line
1367 if (aPt0.Y()>aBewareRect1.Top() && aPt0.Y()<aBewareRect1.Bottom() &&
1368 ((aPt0.X()<=aBewareRect1.Left() && aPt1b.X()>=aBewareRect1.Right()) ||
1369 (aPt1b.X()<=aBewareRect1.Left() && aPt0.X()>=aBewareRect1.Right()))) nIntersections++;
1370 if (aPt0.Y()>aBewareRect2.Top() && aPt0.Y()<aBewareRect2.Bottom() &&
1371 ((aPt0.X()<=aBewareRect2.Left() && aPt1b.X()>=aBewareRect2.Right()) ||
1372 (aPt1b.X()<=aBewareRect2.Left() && aPt0.X()>=aBewareRect2.Right()))) nIntersections++;
1373 } else { // vertical line
1374 if (aPt0.X()>aBewareRect1.Left() && aPt0.X()<aBewareRect1.Right() &&
1375 ((aPt0.Y()<=aBewareRect1.Top() && aPt1b.Y()>=aBewareRect1.Bottom()) ||
1376 (aPt1b.Y()<=aBewareRect1.Top() && aPt0.Y()>=aBewareRect1.Bottom()))) nIntersections++;
1377 if (aPt0.X()>aBewareRect2.Left() && aPt0.X()<aBewareRect2.Right() &&
1378 ((aPt0.Y()<=aBewareRect2.Top() && aPt1b.Y()>=aBewareRect2.Bottom()) ||
1379 (aPt1b.Y()<=aBewareRect2.Top() && aPt0.Y()>=aBewareRect2.Bottom()))) nIntersections++;
1382 aPt0=aPt1b;
1384 if (nPntAnz<=1) nIntersections++;
1385 nQual0=nQual;
1386 nQual+=(sal_uIntPtr)nIntersections*0x10000000;
1387 if (nQual<nQual0 || nIntersections>15) bOverflow = true;
1389 if (bOverflow || nQual==0xFFFFFFFF) nQual=0xFFFFFFFE;
1390 *pnQuality=nQual;
1392 if (bInfo) { // now apply line offsets to aXP1
1393 if (pInfo->nMiddleLine!=0xFFFF) {
1394 sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(MIDDLELINE,aXP1);
1395 if (pInfo->ImpIsHorzLine(MIDDLELINE,aXP1)) {
1396 aXP1[nIdx].Y()+=pInfo->aMiddleLine.Y();
1397 aXP1[nIdx+1].Y()+=pInfo->aMiddleLine.Y();
1398 } else {
1399 aXP1[nIdx].X()+=pInfo->aMiddleLine.X();
1400 aXP1[nIdx+1].X()+=pInfo->aMiddleLine.X();
1403 if (pInfo->nObj1Lines>=2) {
1404 sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(OBJ1LINE2,aXP1);
1405 if (pInfo->ImpIsHorzLine(OBJ1LINE2,aXP1)) {
1406 aXP1[nIdx].Y()+=pInfo->aObj1Line2.Y();
1407 aXP1[nIdx+1].Y()+=pInfo->aObj1Line2.Y();
1408 } else {
1409 aXP1[nIdx].X()+=pInfo->aObj1Line2.X();
1410 aXP1[nIdx+1].X()+=pInfo->aObj1Line2.X();
1413 if (pInfo->nObj1Lines>=3) {
1414 sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(OBJ1LINE3,aXP1);
1415 if (pInfo->ImpIsHorzLine(OBJ1LINE3,aXP1)) {
1416 aXP1[nIdx].Y()+=pInfo->aObj1Line3.Y();
1417 aXP1[nIdx+1].Y()+=pInfo->aObj1Line3.Y();
1418 } else {
1419 aXP1[nIdx].X()+=pInfo->aObj1Line3.X();
1420 aXP1[nIdx+1].X()+=pInfo->aObj1Line3.X();
1423 if (pInfo->nObj2Lines>=2) {
1424 sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(OBJ2LINE2,aXP1);
1425 if (pInfo->ImpIsHorzLine(OBJ2LINE2,aXP1)) {
1426 aXP1[nIdx].Y()+=pInfo->aObj2Line2.Y();
1427 aXP1[nIdx+1].Y()+=pInfo->aObj2Line2.Y();
1428 } else {
1429 aXP1[nIdx].X()+=pInfo->aObj2Line2.X();
1430 aXP1[nIdx+1].X()+=pInfo->aObj2Line2.X();
1433 if (pInfo->nObj2Lines>=3) {
1434 sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(OBJ2LINE3,aXP1);
1435 if (pInfo->ImpIsHorzLine(OBJ2LINE3,aXP1)) {
1436 aXP1[nIdx].Y()+=pInfo->aObj2Line3.Y();
1437 aXP1[nIdx+1].Y()+=pInfo->aObj2Line3.Y();
1438 } else {
1439 aXP1[nIdx].X()+=pInfo->aObj2Line3.X();
1440 aXP1[nIdx+1].X()+=pInfo->aObj2Line3.X();
1444 // make the connector a bezier curve, if appropriate
1445 if (eKind==SDREDGE_BEZIER && nPntAnz>2) {
1446 Point* pPt1=&aXP1[0];
1447 Point* pPt2=&aXP1[1];
1448 Point* pPt3=&aXP1[nPntAnz-2];
1449 Point* pPt4=&aXP1[nPntAnz-1];
1450 long dx1=pPt2->X()-pPt1->X();
1451 long dy1=pPt2->Y()-pPt1->Y();
1452 long dx2=pPt3->X()-pPt4->X();
1453 long dy2=pPt3->Y()-pPt4->Y();
1454 if (cForm=='L') { // nPntAnz==3
1455 aXP1.SetFlags(1,XPOLY_CONTROL);
1456 Point aPt3(*pPt2);
1457 aXP1.Insert(2,aPt3,XPOLY_CONTROL);
1458 nPntAnz=aXP1.GetPointCount();
1459 pPt1=&aXP1[0];
1460 pPt2=&aXP1[1];
1461 pPt3=&aXP1[nPntAnz-2];
1462 pPt4=&aXP1[nPntAnz-1];
1463 pPt2->X()-=dx1/3;
1464 pPt2->Y()-=dy1/3;
1465 pPt3->X()-=dx2/3;
1466 pPt3->Y()-=dy2/3;
1467 } else if (nPntAnz>=4 && nPntAnz<=6) { // Z or U or ...
1468 // To all others, the end points of the original lines become control
1469 // points for now. Thus, we need to do some more work for nPntAnz>4!
1470 aXP1.SetFlags(1,XPOLY_CONTROL);
1471 aXP1.SetFlags(nPntAnz-2,XPOLY_CONTROL);
1472 // distance x1.5
1473 pPt2->X()+=dx1/2;
1474 pPt2->Y()+=dy1/2;
1475 pPt3->X()+=dx2/2;
1476 pPt3->Y()+=dy2/2;
1477 if (nPntAnz==5) {
1478 // add a control point before and after center
1479 Point aCenter(aXP1[2]);
1480 long dx1b=aCenter.X()-aXP1[1].X();
1481 long dy1b=aCenter.Y()-aXP1[1].Y();
1482 long dx2b=aCenter.X()-aXP1[3].X();
1483 long dy2b=aCenter.Y()-aXP1[3].Y();
1484 aXP1.Insert(2,aCenter,XPOLY_CONTROL);
1485 aXP1.SetFlags(3,XPOLY_SYMMTR);
1486 aXP1.Insert(4,aCenter,XPOLY_CONTROL);
1487 aXP1[2].X()-=dx1b/2;
1488 aXP1[2].Y()-=dy1b/2;
1489 aXP1[3].X()-=(dx1b+dx2b)/4;
1490 aXP1[3].Y()-=(dy1b+dy2b)/4;
1491 aXP1[4].X()-=dx2b/2;
1492 aXP1[4].Y()-=dy2b/2;
1494 if (nPntAnz==6) {
1495 Point aPt1b(aXP1[2]);
1496 Point aPt2b(aXP1[3]);
1497 aXP1.Insert(2,aPt1b,XPOLY_CONTROL);
1498 aXP1.Insert(5,aPt2b,XPOLY_CONTROL);
1499 long dx=aPt1b.X()-aPt2b.X();
1500 long dy=aPt1b.Y()-aPt2b.Y();
1501 aXP1[3].X()-=dx/2;
1502 aXP1[3].Y()-=dy/2;
1503 aXP1.SetFlags(3,XPOLY_SYMMTR);
1504 aXP1.Remove(4,1); // because it's identical with aXP1[3]
1508 return aXP1;
1512 There could be a maximum of 64 different developments with with 5 lines, a
1513 maximum of 32 developments with 4 lines, a maximum of 16 developments with
1514 3 lines, a maximum of 8 developments with 2 lines.
1515 This gives us a total of 124 possibilities.
1516 Normalized for the 1st exit angle to the right, there remain 31 possibilities.
1517 Now, normalizing away the vertical mirroring, we get to a total of 16
1518 characteristic developments with 1 through 5 lines:
1520 1 line (type "I") --
1522 2 lines (type "L") __|
1524 3 lines (type "U") __ (type "Z") _
1525 __| _|
1527 4 lines #1 _| #2 | | #3 |_ #4 | |
1528 _| _| _| _|
1529 Of these, #1 is implausible, #2 is a rotated version of #3. This leaves
1530 #2 (from now on referred to as 4.1) and #4 (from now on referred to as 4.2).
1532 5 lines #1 _| #2 _| #3 ___ #4 _
1533 _| _| _| _| _| |_
1534 _ _ _
1535 #5 |_ #6 |_ #7 _| | #8 ____
1536 _| _| _| |_ _|
1537 Of these, 5.1, 5.2, 5.4 and 5.5 are implausible, 5.7 is a reversed version
1538 of 5.3. This leaves 5.3 (type "4"), 5.6 (type "S") and 5.8 (type "C").
1540 We now have discerned the 9 basic types to cover all 400 possible constellations
1541 of object positions and exit angles. 4 of the 9 types have got a center
1542 line (CL). The number of object margins per object varies between 0 and 3:
1544 CL O1 O2 Note
1545 "I": n 0 0
1546 "L": n 0 0
1547 "U": n 0-1 0-1
1548 "Z": y 0 0
1549 4.2: y 0 1 = U+1, respectively 1+U
1550 4.4: n 0-2 0-2 = Z+1
1551 "4": y 0 2 = Z+2
1552 "S": y 1 1 = 1+Z+1
1553 "C": n 0-3 0-3 = 1+U+1
1556 void SdrEdgeObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
1558 SfxSimpleHint* pSimple=PTR_CAST(SfxSimpleHint,&rHint);
1559 sal_uIntPtr nId=pSimple==0 ? 0 : pSimple->GetId();
1560 bool bDataChg=nId==SFX_HINT_DATACHANGED;
1561 bool bDying=nId==SFX_HINT_DYING;
1562 bool bObj1=aCon1.pObj!=NULL && aCon1.pObj->GetBroadcaster()==&rBC;
1563 bool bObj2=aCon2.pObj!=NULL && aCon2.pObj->GetBroadcaster()==&rBC;
1564 if (bDying && (bObj1 || bObj2)) {
1565 // catch Dying, so AttrObj doesn't start broadcasting
1566 // about an alleged change of template
1567 if (bObj1) aCon1.pObj=NULL;
1568 if (bObj2) aCon2.pObj=NULL;
1569 return;
1571 if ( bObj1 || bObj2 )
1573 bEdgeTrackUserDefined = sal_False;
1575 SdrTextObj::Notify(rBC,rHint);
1576 if (nNotifyingCount==0) { // a locking flag
1577 ((SdrEdgeObj*)this)->nNotifyingCount++;
1578 SdrHint* pSdrHint=PTR_CAST(SdrHint,&rHint);
1579 if (bDataChg) { // StyleSheet changed
1580 ImpSetAttrToEdgeInfo(); // when changing templates, copy values from Pool to aEdgeInfo
1582 if (bDataChg ||
1583 (bObj1 && aCon1.pObj->GetPage()==pPage) ||
1584 (bObj2 && aCon2.pObj->GetPage()==pPage) ||
1585 (pSdrHint && pSdrHint->GetKind()==HINT_OBJREMOVED))
1587 // broadcasting only, if on the same page
1588 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect();
1589 ImpDirtyEdgeTrack();
1591 // only redraw here, object hasn't actually changed
1592 ActionChanged();
1594 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0);
1596 ((SdrEdgeObj*)this)->nNotifyingCount--;
1600 /** updates edges that are connected to the edges of this object
1601 as if the connected objects sent a repaint broadcast
1603 void SdrEdgeObj::Reformat()
1605 if( NULL != aCon1.pObj )
1607 SfxSimpleHint aHint( SFX_HINT_DATACHANGED );
1608 Notify( *const_cast<SfxBroadcaster*>(aCon1.pObj->GetBroadcaster()), aHint );
1611 if( NULL != aCon2.pObj )
1613 SfxSimpleHint aHint( SFX_HINT_DATACHANGED );
1614 Notify( *const_cast<SfxBroadcaster*>(aCon2.pObj->GetBroadcaster()), aHint );
1618 SdrEdgeObj* SdrEdgeObj::Clone() const
1620 return CloneHelper< SdrEdgeObj >();
1623 SdrEdgeObj& SdrEdgeObj::operator=(const SdrEdgeObj& rObj)
1625 if( this == &rObj )
1626 return *this;
1627 SdrTextObj::operator=(rObj);
1628 *pEdgeTrack =*rObj.pEdgeTrack;
1629 bEdgeTrackDirty=rObj.bEdgeTrackDirty;
1630 aCon1 =rObj.aCon1;
1631 aCon2 =rObj.aCon2;
1632 aCon1.pObj=NULL;
1633 aCon2.pObj=NULL;
1634 aEdgeInfo=rObj.aEdgeInfo;
1635 return *this;
1638 void SdrEdgeObj::TakeObjNameSingul(XubString& rName) const
1640 rName=ImpGetResStr(STR_ObjNameSingulEDGE);
1642 String aName( GetName() );
1643 if(aName.Len())
1645 rName += sal_Unicode(' ');
1646 rName += sal_Unicode('\'');
1647 rName += aName;
1648 rName += sal_Unicode('\'');
1652 void SdrEdgeObj::TakeObjNamePlural(XubString& rName) const
1654 rName=ImpGetResStr(STR_ObjNamePluralEDGE);
1657 basegfx::B2DPolyPolygon SdrEdgeObj::TakeXorPoly() const
1659 basegfx::B2DPolyPolygon aPolyPolygon;
1661 if (bEdgeTrackDirty)
1663 ((SdrEdgeObj*)this)->ImpRecalcEdgeTrack();
1666 if(pEdgeTrack)
1668 aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
1671 return aPolyPolygon;
1674 void SdrEdgeObj::SetEdgeTrackPath( const basegfx::B2DPolyPolygon& rPoly )
1676 if ( !rPoly.count() )
1678 bEdgeTrackDirty = sal_True;
1679 bEdgeTrackUserDefined = sal_False;
1681 else
1683 *pEdgeTrack = XPolygon( rPoly.getB2DPolygon( 0 ) );
1684 bEdgeTrackDirty = sal_False;
1685 bEdgeTrackUserDefined = sal_True;
1687 // #i110629# also set aRect and maSnapeRect depending on pEdgeTrack
1688 const Rectangle aPolygonBounds(pEdgeTrack->GetBoundRect());
1689 aRect = aPolygonBounds;
1690 maSnapRect = aPolygonBounds;
1694 basegfx::B2DPolyPolygon SdrEdgeObj::GetEdgeTrackPath() const
1696 basegfx::B2DPolyPolygon aPolyPolygon;
1698 if (bEdgeTrackDirty)
1699 ((SdrEdgeObj*)this)->ImpRecalcEdgeTrack();
1701 aPolyPolygon.append( pEdgeTrack->getB2DPolygon() );
1703 return aPolyPolygon;
1706 sal_uInt32 SdrEdgeObj::GetHdlCount() const
1708 SdrEdgeKind eKind=((SdrEdgeKindItem&)(GetObjectItem(SDRATTR_EDGEKIND))).GetValue();
1709 sal_uInt32 nHdlAnz(0L);
1710 sal_uInt32 nPntAnz(pEdgeTrack->GetPointCount());
1712 if(nPntAnz)
1714 nHdlAnz = 2L;
1716 if ((eKind==SDREDGE_ORTHOLINES || eKind==SDREDGE_BEZIER) && nPntAnz >= 4L)
1718 sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0L ? aEdgeInfo.nObj1Lines - 1L : 0L);
1719 sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0L ? aEdgeInfo.nObj2Lines - 1L : 0L);
1720 sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1L : 0L);
1721 nHdlAnz += nO1 + nO2 + nM;
1723 else if (eKind==SDREDGE_THREELINES && nPntAnz == 4L)
1725 if(GetConnectedNode(sal_True))
1726 nHdlAnz++;
1728 if(GetConnectedNode(sal_False))
1729 nHdlAnz++;
1733 return nHdlAnz;
1736 SdrHdl* SdrEdgeObj::GetHdl(sal_uInt32 nHdlNum) const
1738 SdrHdl* pHdl=NULL;
1739 sal_uInt32 nPntAnz(pEdgeTrack->GetPointCount());
1740 if (nPntAnz!=0) {
1741 if (nHdlNum==0) {
1742 pHdl=new ImpEdgeHdl((*pEdgeTrack)[0],HDL_POLY);
1743 if (aCon1.pObj!=NULL && aCon1.bBestVertex) pHdl->Set1PixMore(sal_True);
1744 } else if (nHdlNum==1) {
1745 pHdl=new ImpEdgeHdl((*pEdgeTrack)[sal_uInt16(nPntAnz-1)],HDL_POLY);
1746 if (aCon2.pObj!=NULL && aCon2.bBestVertex) pHdl->Set1PixMore(sal_True);
1747 } else {
1748 SdrEdgeKind eKind=((SdrEdgeKindItem&)(GetObjectItem(SDRATTR_EDGEKIND))).GetValue();
1749 if (eKind==SDREDGE_ORTHOLINES || eKind==SDREDGE_BEZIER) {
1750 sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0L ? aEdgeInfo.nObj1Lines - 1L : 0L);
1751 sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0L ? aEdgeInfo.nObj2Lines - 1L : 0L);
1752 sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1L : 0L);
1753 sal_uInt32 nNum(nHdlNum - 2L);
1754 sal_Int32 nPt(0L);
1755 pHdl=new ImpEdgeHdl(Point(),HDL_POLY);
1756 if (nNum<nO1) {
1757 nPt=nNum+1L;
1758 if (nNum==0) ((ImpEdgeHdl*)pHdl)->SetLineCode(OBJ1LINE2);
1759 if (nNum==1) ((ImpEdgeHdl*)pHdl)->SetLineCode(OBJ1LINE3);
1760 } else {
1761 nNum=nNum-nO1;
1762 if (nNum<nO2) {
1763 nPt=nPntAnz-3-nNum;
1764 if (nNum==0) ((ImpEdgeHdl*)pHdl)->SetLineCode(OBJ2LINE2);
1765 if (nNum==1) ((ImpEdgeHdl*)pHdl)->SetLineCode(OBJ2LINE3);
1766 } else {
1767 nNum=nNum-nO2;
1768 if (nNum<nM) {
1769 nPt=aEdgeInfo.nMiddleLine;
1770 ((ImpEdgeHdl*)pHdl)->SetLineCode(MIDDLELINE);
1774 if (nPt>0) {
1775 Point aPos((*pEdgeTrack)[(sal_uInt16)nPt]);
1776 aPos+=(*pEdgeTrack)[(sal_uInt16)nPt+1];
1777 aPos.X()/=2;
1778 aPos.Y()/=2;
1779 pHdl->SetPos(aPos);
1780 } else {
1781 delete pHdl;
1782 pHdl=NULL;
1784 } else if (eKind==SDREDGE_THREELINES) {
1785 sal_uInt32 nNum(nHdlNum);
1786 if (GetConnectedNode(sal_True)==NULL) nNum++;
1787 Point aPos((*pEdgeTrack)[(sal_uInt16)nNum-1]);
1788 pHdl=new ImpEdgeHdl(aPos,HDL_POLY);
1789 if (nNum==2) ((ImpEdgeHdl*)pHdl)->SetLineCode(OBJ1LINE2);
1790 if (nNum==3) ((ImpEdgeHdl*)pHdl)->SetLineCode(OBJ2LINE2);
1793 if (pHdl!=NULL) {
1794 pHdl->SetPointNum(nHdlNum);
1797 return pHdl;
1800 ////////////////////////////////////////////////////////////////////////////////////////////////////
1802 bool SdrEdgeObj::hasSpecialDrag() const
1804 return true;
1807 SdrObject* SdrEdgeObj::getFullDragClone() const
1809 // use Clone operator
1810 SdrEdgeObj* pRetval = (SdrEdgeObj*)Clone();
1812 // copy connections for clone, SdrEdgeObj::operator= does not do this
1813 pRetval->ConnectToNode(true, GetConnectedNode(true));
1814 pRetval->ConnectToNode(false, GetConnectedNode(false));
1816 return pRetval;
1819 bool SdrEdgeObj::beginSpecialDrag(SdrDragStat& rDrag) const
1821 if(!rDrag.GetHdl())
1822 return false;
1824 rDrag.SetEndDragChangesAttributes(true);
1826 if(rDrag.GetHdl()->GetPointNum() < 2)
1828 rDrag.SetNoSnap(true);
1831 return true;
1834 bool SdrEdgeObj::applySpecialDrag(SdrDragStat& rDragStat)
1836 SdrEdgeObj* pOriginalEdge = dynamic_cast< SdrEdgeObj* >(rDragStat.GetHdl()->GetObj());
1837 const bool bOriginalEdgeModified(pOriginalEdge == this);
1839 if(!bOriginalEdgeModified && pOriginalEdge)
1841 // copy connections when clone is modified. This is needed because
1842 // as preparation to this modification the data from the original object
1843 // was copied to the clone using the operator=. As can be seen there,
1844 // that operator does not copy the connections (for good reason)
1845 ConnectToNode(true, pOriginalEdge->GetConnection(true).GetObject());
1846 ConnectToNode(false, pOriginalEdge->GetConnection(false).GetObject());
1849 if(rDragStat.GetHdl()->GetPointNum() < 2)
1851 // start or end point connector drag
1852 const bool bDragA(0 == rDragStat.GetHdl()->GetPointNum());
1853 const Point aPointNow(rDragStat.GetNow());
1855 if(rDragStat.GetPageView())
1857 SdrObjConnection* pDraggedOne(bDragA ? &aCon1 : &aCon2);
1859 // clear connection
1860 DisconnectFromNode(bDragA);
1862 // look for new connection
1863 ImpFindConnector(aPointNow, *rDragStat.GetPageView(), *pDraggedOne, pOriginalEdge);
1865 if(pDraggedOne->pObj)
1867 // if found, officially connect to it; ImpFindConnector only
1868 // sets pObj hard
1869 SdrObject* pNewConnection = pDraggedOne->pObj;
1870 pDraggedOne->pObj = 0;
1871 ConnectToNode(bDragA, pNewConnection);
1874 if(rDragStat.GetView() && !bOriginalEdgeModified)
1876 // show IA helper, but only do this during IA, so not when the original
1877 // Edge gets modified in the last call
1878 rDragStat.GetView()->SetConnectMarker(*pDraggedOne, *rDragStat.GetPageView());
1882 if(pEdgeTrack)
1884 // change pEdgeTrack to modified position
1885 if(bDragA)
1887 (*pEdgeTrack)[0] = aPointNow;
1889 else
1891 (*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount()-1)] = aPointNow;
1895 // reset edge info's offsets, this is a end point drag
1896 aEdgeInfo.aObj1Line2 = Point();
1897 aEdgeInfo.aObj1Line3 = Point();
1898 aEdgeInfo.aObj2Line2 = Point();
1899 aEdgeInfo.aObj2Line3 = Point();
1900 aEdgeInfo.aMiddleLine = Point();
1902 else
1904 // control point connector drag
1905 const ImpEdgeHdl* pEdgeHdl = (ImpEdgeHdl*)rDragStat.GetHdl();
1906 const SdrEdgeLineCode eLineCode = pEdgeHdl->GetLineCode();
1907 const Point aDist(rDragStat.GetNow() - rDragStat.GetStart());
1908 sal_Int32 nDist(pEdgeHdl->IsHorzDrag() ? aDist.X() : aDist.Y());
1910 nDist += aEdgeInfo.ImpGetLineVersatz(eLineCode, *pEdgeTrack);
1911 aEdgeInfo.ImpSetLineVersatz(eLineCode, *pEdgeTrack, nDist);
1914 // force recalculation of EdgeTrack
1915 *pEdgeTrack = ImpCalcEdgeTrack(*pEdgeTrack, aCon1, aCon2, &aEdgeInfo);
1916 bEdgeTrackDirty=sal_False;
1918 // save EdgeInfos and mark object as user modified
1919 ImpSetEdgeInfoToAttr();
1920 bEdgeTrackUserDefined = false;
1922 if(bOriginalEdgeModified && rDragStat.GetView())
1924 // hide connect marker helper again when original gets changed.
1925 // This happens at the end of the interaction
1926 rDragStat.GetView()->HideConnectMarker();
1929 return true;
1932 String SdrEdgeObj::getSpecialDragComment(const SdrDragStat& rDrag) const
1934 const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
1936 if(bCreateComment)
1938 return String();
1940 else
1942 rtl::OUString aStr;
1943 ImpTakeDescriptionStr(STR_DragEdgeTail, aStr);
1945 return aStr;
1949 ////////////////////////////////////////////////////////////////////////////////////////////////////
1951 basegfx::B2DPolygon SdrEdgeObj::ImplAddConnectorOverlay(SdrDragMethod& rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
1953 basegfx::B2DPolygon aResult;
1955 if(bDetail)
1957 SdrObjConnection aMyCon1(aCon1);
1958 SdrObjConnection aMyCon2(aCon2);
1960 if (bTail1)
1962 const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon1.aObjOfs.X(), aMyCon1.aObjOfs.Y()));
1963 aMyCon1.aObjOfs.X() = basegfx::fround(aTemp.getX());
1964 aMyCon1.aObjOfs.Y() = basegfx::fround(aTemp.getY());
1967 if (bTail2)
1969 const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon2.aObjOfs.X(), aMyCon2.aObjOfs.Y()));
1970 aMyCon2.aObjOfs.X() = basegfx::fround(aTemp.getX());
1971 aMyCon2.aObjOfs.Y() = basegfx::fround(aTemp.getY());
1974 SdrEdgeInfoRec aInfo(aEdgeInfo);
1975 XPolygon aXP(ImpCalcEdgeTrack(*pEdgeTrack, aMyCon1, aMyCon2, &aInfo));
1977 if(aXP.GetPointCount())
1979 aResult = aXP.getB2DPolygon();
1982 else
1984 Point aPt1((*pEdgeTrack)[0]);
1985 Point aPt2((*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount() - 1)]);
1987 if (aCon1.pObj && (aCon1.bBestConn || aCon1.bBestVertex))
1988 aPt1 = aCon1.pObj->GetSnapRect().Center();
1990 if (aCon2.pObj && (aCon2.bBestConn || aCon2.bBestVertex))
1991 aPt2 = aCon2.pObj->GetSnapRect().Center();
1993 if (bTail1)
1995 const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
1996 aPt1.X() = basegfx::fround(aTemp.getX());
1997 aPt1.Y() = basegfx::fround(aTemp.getY());
2000 if (bTail2)
2002 const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
2003 aPt2.X() = basegfx::fround(aTemp.getX());
2004 aPt2.Y() = basegfx::fround(aTemp.getY());
2007 aResult.append(basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
2008 aResult.append(basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
2011 return aResult;
2014 bool SdrEdgeObj::BegCreate(SdrDragStat& rDragStat)
2016 rDragStat.SetNoSnap(sal_True);
2017 pEdgeTrack->SetPointCount(2);
2018 (*pEdgeTrack)[0]=rDragStat.GetStart();
2019 (*pEdgeTrack)[1]=rDragStat.GetNow();
2020 if (rDragStat.GetPageView()!=NULL) {
2021 ImpFindConnector(rDragStat.GetStart(),*rDragStat.GetPageView(),aCon1,this);
2022 ConnectToNode(sal_True,aCon1.pObj);
2024 *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
2025 return sal_True;
2028 bool SdrEdgeObj::MovCreate(SdrDragStat& rDragStat)
2030 sal_uInt16 nMax=pEdgeTrack->GetPointCount();
2031 (*pEdgeTrack)[nMax-1]=rDragStat.GetNow();
2032 if (rDragStat.GetPageView()!=NULL) {
2033 ImpFindConnector(rDragStat.GetNow(),*rDragStat.GetPageView(),aCon2,this);
2034 rDragStat.GetView()->SetConnectMarker(aCon2,*rDragStat.GetPageView());
2036 SetBoundRectDirty();
2037 bSnapRectDirty=sal_True;
2038 ConnectToNode(sal_False,aCon2.pObj);
2039 *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
2040 bEdgeTrackDirty=sal_False;
2041 return sal_True;
2044 bool SdrEdgeObj::EndCreate(SdrDragStat& rDragStat, SdrCreateCmd eCmd)
2046 bool bOk=(eCmd==SDRCREATE_FORCEEND || rDragStat.GetPointAnz()>=2);
2047 if (bOk) {
2048 ConnectToNode(sal_True,aCon1.pObj);
2049 ConnectToNode(sal_False,aCon2.pObj);
2050 if (rDragStat.GetView()!=NULL) {
2051 rDragStat.GetView()->HideConnectMarker();
2053 ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
2055 SetRectsDirty();
2056 return bOk;
2059 bool SdrEdgeObj::BckCreate(SdrDragStat& rDragStat)
2061 if (rDragStat.GetView()!=NULL) {
2062 rDragStat.GetView()->HideConnectMarker();
2064 return sal_False;
2067 void SdrEdgeObj::BrkCreate(SdrDragStat& rDragStat)
2069 if (rDragStat.GetView()!=NULL) {
2070 rDragStat.GetView()->HideConnectMarker();
2074 basegfx::B2DPolyPolygon SdrEdgeObj::TakeCreatePoly(const SdrDragStat& /*rStatDrag*/) const
2076 basegfx::B2DPolyPolygon aRetval;
2077 aRetval.append(pEdgeTrack->getB2DPolygon());
2078 return aRetval;
2081 Pointer SdrEdgeObj::GetCreatePointer() const
2083 return Pointer(POINTER_DRAW_CONNECT);
2086 bool SdrEdgeObj::ImpFindConnector(const Point& rPt, const SdrPageView& rPV, SdrObjConnection& rCon, const SdrEdgeObj* pThis, OutputDevice* pOut)
2088 rCon.ResetVars();
2089 if (pOut==NULL) pOut=rPV.GetView().GetFirstOutputDevice();
2090 if (pOut==NULL) return sal_False;
2091 SdrObjList* pOL=rPV.GetObjList();
2092 const SetOfByte& rVisLayer=rPV.GetVisibleLayers();
2093 // sensitive area of connectors is twice as large as the one of the handles
2094 sal_uInt16 nMarkHdSiz=rPV.GetView().GetMarkHdlSizePixel();
2095 Size aHalfConSiz(nMarkHdSiz,nMarkHdSiz);
2096 aHalfConSiz=pOut->PixelToLogic(aHalfConSiz);
2097 Size aHalfCenterSiz(2*aHalfConSiz.Width(),2*aHalfConSiz.Height());
2098 Rectangle aMouseRect(rPt,rPt);
2099 aMouseRect.Left() -=aHalfConSiz.Width();
2100 aMouseRect.Top() -=aHalfConSiz.Height();
2101 aMouseRect.Right() +=aHalfConSiz.Width();
2102 aMouseRect.Bottom()+=aHalfConSiz.Height();
2103 sal_uInt16 nBoundHitTol=(sal_uInt16)aHalfConSiz.Width()/2; if (nBoundHitTol==0) nBoundHitTol=1;
2104 sal_uIntPtr no=pOL->GetObjCount();
2105 bool bFnd = false;
2106 SdrObjConnection aTestCon;
2107 SdrObjConnection aBestCon;
2109 while (no>0 && !bFnd) {
2110 // issue: group objects on different layers return LayerID=0!
2111 no--;
2112 SdrObject* pObj=pOL->GetObj(no);
2113 if (rVisLayer.IsSet(pObj->GetLayer()) && pObj->IsVisible() && // only visible objects
2114 (pThis==NULL || pObj!=(SdrObject*)pThis) && // don't connect it to itself
2115 pObj->IsNode())
2117 Rectangle aObjBound(pObj->GetCurrentBoundRect());
2118 if (aObjBound.IsOver(aMouseRect)) {
2119 aTestCon.ResetVars();
2120 bool bEdge=HAS_BASE(SdrEdgeObj,pObj); // no BestCon for Edge
2121 // User-defined connectors have absolute priority.
2122 // After those come Vertex, Corner and center (Best), all prioritized equally.
2123 // Finally, a HitTest for the object.
2124 const SdrGluePointList* pGPL=pObj->GetGluePointList();
2125 sal_uInt16 nConAnz=pGPL==NULL ? 0 : pGPL->GetCount();
2126 sal_uInt16 nGesAnz=nConAnz+9;
2127 bool bUserFnd = false;
2128 sal_uIntPtr nBestDist=0xFFFFFFFF;
2129 for (sal_uInt16 i=0; i<nGesAnz; i++)
2131 bool bUser=i<nConAnz;
2132 bool bVertex=i>=nConAnz+0 && i<nConAnz+4;
2133 bool bCorner=i>=nConAnz+4 && i<nConAnz+8;
2134 bool bCenter=i==nConAnz+8;
2135 bool bOk = false;
2136 Point aConPos;
2137 sal_uInt16 nConNum=i;
2138 if (bUser) {
2139 const SdrGluePoint& rGP=(*pGPL)[nConNum];
2140 aConPos=rGP.GetAbsolutePos(*pObj);
2141 nConNum=rGP.GetId();
2142 bOk = true;
2143 } else if (bVertex && !bUserFnd) {
2144 nConNum=nConNum-nConAnz;
2145 if (rPV.GetView().IsAutoVertexConnectors()) {
2146 SdrGluePoint aPt(pObj->GetVertexGluePoint(nConNum));
2147 aConPos=aPt.GetAbsolutePos(*pObj);
2148 bOk = true;
2149 } else i+=3;
2150 } else if (bCorner && !bUserFnd) {
2151 nConNum-=nConAnz+4;
2152 if (rPV.GetView().IsAutoCornerConnectors()) {
2153 SdrGluePoint aPt(pObj->GetCornerGluePoint(nConNum));
2154 aConPos=aPt.GetAbsolutePos(*pObj);
2155 bOk = true;
2156 } else i+=3;
2158 else if (bCenter && !bUserFnd && !bEdge)
2160 // Suppress default connect at object center
2161 if(!pThis || !pThis->GetSuppressDefaultConnect())
2163 // not the edges!
2164 nConNum=0;
2165 aConPos=aObjBound.Center();
2166 bOk = true;
2169 if (bOk && aMouseRect.IsInside(aConPos)) {
2170 if (bUser) bUserFnd = true;
2171 bFnd = true;
2172 sal_uIntPtr nDist=(sal_uIntPtr)Abs(aConPos.X()-rPt.X())+(sal_uIntPtr)Abs(aConPos.Y()-rPt.Y());
2173 if (nDist<nBestDist) {
2174 nBestDist=nDist;
2175 aTestCon.pObj=pObj;
2176 aTestCon.nConId=nConNum;
2177 aTestCon.bAutoCorner=bCorner;
2178 aTestCon.bAutoVertex=bVertex;
2179 aTestCon.bBestConn=sal_False; // bCenter;
2180 aTestCon.bBestVertex=bCenter;
2184 // if no connector is hit, try HitTest again, for BestConnector (=bCenter)
2185 if(!bFnd &&
2186 !bEdge &&
2187 SdrObjectPrimitiveHit(*pObj, rPt, nBoundHitTol, rPV, &rVisLayer, false))
2189 // Suppress default connect at object inside bound
2190 if(!pThis || !pThis->GetSuppressDefaultConnect())
2192 bFnd = true;
2193 aTestCon.pObj=pObj;
2194 aTestCon.bBestConn=sal_True;
2197 if (bFnd) {
2198 Rectangle aMouseRect2(rPt,rPt);
2199 aMouseRect.Left() -=nBoundHitTol;
2200 aMouseRect.Top() -=nBoundHitTol;
2201 aMouseRect.Right() +=nBoundHitTol;
2202 aMouseRect.Bottom()+=nBoundHitTol;
2203 aObjBound.IsOver(aMouseRect2);
2209 rCon=aTestCon;
2210 return bFnd;
2213 void SdrEdgeObj::NbcSetSnapRect(const Rectangle& rRect)
2215 const Rectangle aOld(GetSnapRect());
2217 if(aOld != rRect)
2219 if(aRect.IsEmpty() && 0 == pEdgeTrack->GetPointCount())
2221 // #i110629# When initializing, do not scale on empty Rectangle; this
2222 // will mirror the underlying text object (!)
2223 aRect = rRect;
2224 maSnapRect = rRect;
2226 else
2228 long nMulX = rRect.Right() - rRect.Left();
2229 long nDivX = aOld.Right() - aOld.Left();
2230 long nMulY = rRect.Bottom() - rRect.Top();
2231 long nDivY = aOld.Bottom() - aOld.Top();
2232 if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2233 if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2234 Fraction aX(nMulX, nDivX);
2235 Fraction aY(nMulY, nDivY);
2236 NbcResize(aOld.TopLeft(), aX, aY);
2237 NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2242 void SdrEdgeObj::NbcMove(const Size& rSiz)
2244 SdrTextObj::NbcMove(rSiz);
2245 MoveXPoly(*pEdgeTrack,rSiz);
2248 void SdrEdgeObj::NbcResize(const Point& rRefPnt, const Fraction& aXFact, const Fraction& aYFact)
2250 SdrTextObj::NbcResize(rRefPnt,aXFact,aXFact);
2251 ResizeXPoly(*pEdgeTrack,rRefPnt,aXFact,aYFact);
2253 // if resize is not from paste, forget user distances
2254 if(!GetModel()->IsPasteResize())
2256 aEdgeInfo.aObj1Line2 = Point();
2257 aEdgeInfo.aObj1Line3 = Point();
2258 aEdgeInfo.aObj2Line2 = Point();
2259 aEdgeInfo.aObj2Line3 = Point();
2260 aEdgeInfo.aMiddleLine = Point();
2264 SdrObject* SdrEdgeObj::DoConvertToPolyObj(sal_Bool bBezier) const
2266 basegfx::B2DPolyPolygon aPolyPolygon;
2267 aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
2268 SdrObject* pRet = ImpConvertMakeObj(aPolyPolygon, sal_False, bBezier);
2269 pRet = ImpConvertAddText(pRet, bBezier);
2271 return pRet;
2274 sal_uInt32 SdrEdgeObj::GetSnapPointCount() const
2276 return 2L;
2279 Point SdrEdgeObj::GetSnapPoint(sal_uInt32 i) const
2281 ((SdrEdgeObj*)this)->ImpUndirtyEdgeTrack();
2282 sal_uInt16 nAnz=pEdgeTrack->GetPointCount();
2283 if (i==0) return (*pEdgeTrack)[0];
2284 else return (*pEdgeTrack)[nAnz-1];
2287 sal_Bool SdrEdgeObj::IsPolyObj() const
2289 return sal_False;
2292 sal_uInt32 SdrEdgeObj::GetPointCount() const
2294 return 0L;
2297 Point SdrEdgeObj::GetPoint(sal_uInt32 i) const
2299 ((SdrEdgeObj*)this)->ImpUndirtyEdgeTrack();
2300 sal_uInt16 nAnz=pEdgeTrack->GetPointCount();
2301 if (0L == i)
2302 return (*pEdgeTrack)[0];
2303 else
2304 return (*pEdgeTrack)[nAnz-1];
2307 void SdrEdgeObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
2309 // TODO: Need an implementation to connect differently.
2310 ImpUndirtyEdgeTrack();
2311 sal_uInt16 nAnz=pEdgeTrack->GetPointCount();
2312 if (0L == i)
2313 (*pEdgeTrack)[0]=rPnt;
2314 if (1L == i)
2315 (*pEdgeTrack)[nAnz-1]=rPnt;
2316 SetEdgeTrackDirty();
2317 SetRectsDirty();
2320 SdrEdgeObjGeoData::SdrEdgeObjGeoData()
2322 pEdgeTrack=new XPolygon;
2325 SdrEdgeObjGeoData::~SdrEdgeObjGeoData()
2327 delete pEdgeTrack;
2330 SdrObjGeoData* SdrEdgeObj::NewGeoData() const
2332 return new SdrEdgeObjGeoData;
2335 void SdrEdgeObj::SaveGeoData(SdrObjGeoData& rGeo) const
2337 SdrTextObj::SaveGeoData(rGeo);
2338 SdrEdgeObjGeoData& rEGeo=(SdrEdgeObjGeoData&)rGeo;
2339 rEGeo.aCon1 =aCon1;
2340 rEGeo.aCon2 =aCon2;
2341 *rEGeo.pEdgeTrack =*pEdgeTrack;
2342 rEGeo.bEdgeTrackDirty=bEdgeTrackDirty;
2343 rEGeo.bEdgeTrackUserDefined=bEdgeTrackUserDefined;
2344 rEGeo.aEdgeInfo =aEdgeInfo;
2347 void SdrEdgeObj::RestGeoData(const SdrObjGeoData& rGeo)
2349 SdrTextObj::RestGeoData(rGeo);
2350 SdrEdgeObjGeoData& rEGeo=(SdrEdgeObjGeoData&)rGeo;
2351 if (aCon1.pObj!=rEGeo.aCon1.pObj) {
2352 if (aCon1.pObj!=NULL) aCon1.pObj->RemoveListener(*this);
2353 aCon1=rEGeo.aCon1;
2354 if (aCon1.pObj!=NULL) aCon1.pObj->AddListener(*this);
2356 if (aCon2.pObj!=rEGeo.aCon2.pObj) {
2357 if (aCon2.pObj!=NULL) aCon2.pObj->RemoveListener(*this);
2358 aCon2=rEGeo.aCon2;
2359 if (aCon2.pObj!=NULL) aCon2.pObj->AddListener(*this);
2361 *pEdgeTrack =*rEGeo.pEdgeTrack;
2362 bEdgeTrackDirty=rEGeo.bEdgeTrackDirty;
2363 bEdgeTrackUserDefined=rEGeo.bEdgeTrackUserDefined;
2364 aEdgeInfo =rEGeo.aEdgeInfo;
2367 Point SdrEdgeObj::GetTailPoint( sal_Bool bTail ) const
2369 if( pEdgeTrack && pEdgeTrack->GetPointCount()!=0)
2371 const XPolygon& rTrack0 = *pEdgeTrack;
2372 if(bTail)
2374 return rTrack0[0];
2376 else
2378 const sal_uInt16 nSiz = rTrack0.GetPointCount() - 1;
2379 return rTrack0[nSiz];
2382 else
2384 if(bTail)
2385 return aOutRect.TopLeft();
2386 else
2387 return aOutRect.BottomRight();
2392 void SdrEdgeObj::SetTailPoint( sal_Bool bTail, const Point& rPt )
2394 ImpSetTailPoint( bTail, rPt );
2395 SetChanged();
2398 /** this method is used by the api to set a glue point for a connection
2399 nId == -1 : The best default point is automatically chosen
2400 0 <= nId <= 3 : One of the default points is chosen
2401 nId >= 4 : A user defined glue point is chosen
2403 void SdrEdgeObj::setGluePointIndex( sal_Bool bTail, sal_Int32 nIndex /* = -1 */ )
2405 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetCurrentBoundRect();
2407 SdrObjConnection& rConn1 = GetConnection( bTail );
2409 rConn1.SetAutoVertex( nIndex >= 0 && nIndex <= 3 );
2410 rConn1.SetBestConnection( nIndex < 0 );
2411 rConn1.SetBestVertex( nIndex < 0 );
2413 if( nIndex > 3 )
2415 nIndex -= 3; // the start api index is 0, whereas the implementation in svx starts from 1
2417 // for user defined glue points we have
2418 // to get the id for this index first
2419 const SdrGluePointList* pList = rConn1.GetObject() ? rConn1.GetObject()->GetGluePointList() : NULL;
2420 if( pList == NULL || SDRGLUEPOINT_NOTFOUND == pList->FindGluePoint((sal_uInt16)nIndex) )
2421 return;
2423 else if( nIndex < 0 )
2425 nIndex = 0;
2428 rConn1.SetConnectorId( (sal_uInt16)nIndex );
2430 SetChanged();
2431 SetRectsDirty();
2432 ImpRecalcEdgeTrack();
2435 /** this method is used by the api to return a glue point id for a connection.
2436 See setGluePointId for possible return values */
2437 sal_Int32 SdrEdgeObj::getGluePointIndex( sal_Bool bTail )
2439 SdrObjConnection& rConn1 = GetConnection( bTail );
2440 sal_Int32 nId = -1;
2441 if( !rConn1.IsBestConnection() )
2443 nId = rConn1.GetConnectorId();
2444 if( !rConn1.IsAutoVertex() )
2445 nId += 3; // the start api index is 0, whereas the implementation in svx starts from 1
2447 return nId;
2450 // Implementation was missing; edge track needs to be invalidated additionally.
2451 void SdrEdgeObj::NbcSetAnchorPos(const Point& rPnt)
2453 // call parent functionality
2454 SdrTextObj::NbcSetAnchorPos(rPnt);
2456 // Additionally, invalidate edge track
2457 ImpDirtyEdgeTrack();
2460 sal_Bool SdrEdgeObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
2462 // use base method from SdrObject, it's not rotatable and
2463 // a call to GetSnapRect() is used. That's what we need for Connector.
2464 return SdrObject::TRGetBaseGeometry(rMatrix, rPolyPolygon);
2467 void SdrEdgeObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
2469 // where appropriate take care for existing connections. For now, just use the
2470 // implementation from SdrObject.
2471 SdrObject::TRSetBaseGeometry(rMatrix, rPolyPolygon);
2474 // for geometry access
2475 ::basegfx::B2DPolygon SdrEdgeObj::getEdgeTrack() const
2477 if(bEdgeTrackDirty)
2479 const_cast< SdrEdgeObj* >(this)->ImpRecalcEdgeTrack();
2482 if(pEdgeTrack)
2484 return pEdgeTrack->getB2DPolygon();
2486 else
2488 return ::basegfx::B2DPolygon();
2492 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */