bump product version to 4.1.6.2
[LibreOffice.git] / svx / source / svdraw / svdoedge.cxx
blob98c57f9516ca43114a0dd8a6300fd39653d2e8cf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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()
56 pObj=NULL;
57 nConId=0;
58 nXDist=0;
59 nYDist=0;
60 bBestConn=sal_True;
61 bBestVertex=sal_True;
62 bXDistOvr=sal_False;
63 bYDistOvr=sal_False;
64 bAutoVertex=sal_False;
65 bAutoCorner=sal_False;
68 bool SdrObjConnection::TakeGluePoint(SdrGluePoint& rGP, bool bSetAbsPos) const
70 bool bRet = false;
71 if (pObj!=NULL) { // one object has to be docked already!
72 if (bAutoVertex) {
73 rGP=pObj->GetVertexGluePoint(nConId);
74 bRet = true;
75 } else if (bAutoCorner) {
76 rGP=pObj->GetCornerGluePoint(nConId);
77 bRet = true;
78 } else {
79 const SdrGluePointList* pGPL=pObj->GetGluePointList();
80 if (pGPL!=NULL) {
81 sal_uInt16 nNum=pGPL->FindGluePoint(nConId);
82 if (nNum!=SDRGLUEPOINT_NOTFOUND) {
83 rGP=(*pGPL)[nNum];
84 bRet = true;
89 if (bRet && bSetAbsPos) {
90 Point aPt(rGP.GetAbsolutePos(*pObj));
91 aPt+=aObjOfs;
92 rGP.SetPos(aPt);
94 return bRet;
97 Point& SdrEdgeInfoRec::ImpGetLineVersatzPoint(SdrEdgeLineCode eLineCode)
99 switch (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;
105 } // switch
106 return aMiddleLine;
109 sal_uInt16 SdrEdgeInfoRec::ImpGetPolyIdx(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
111 switch (eLineCode) {
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;
117 } // switch
118 return 0;
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;
130 return 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;
137 else rPt.X()=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();
144 else return rPt.X();
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()
168 : SdrTextObj(),
169 nNotifyingCount(0),
170 bEdgeTrackDirty(sal_False),
171 bEdgeTrackUserDefined(sal_False),
172 // Default is to allow default connects
173 mbSuppressDefaultConnect(sal_False),
174 mbBoundRectCalculationRunning(sal_False),
175 mbSuppressed(false)
177 bClosedObj=sal_False;
178 bIsEdge=sal_True;
179 pEdgeTrack=new XPolygon;
183 SdrEdgeObj::~SdrEdgeObj()
185 DisconnectFromNode(sal_True);
186 DisconnectFromNode(sal_False);
187 delete pEdgeTrack;
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 };
201 sal_uInt16 n = 0;
203 if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
205 aEdgeInfo.ImpSetLineVersatz(OBJ1LINE2, *pEdgeTrack, nVals[n]);
206 n++;
209 if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
211 aEdgeInfo.ImpSetLineVersatz(OBJ1LINE3, *pEdgeTrack, nVals[n]);
212 n++;
215 if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
217 aEdgeInfo.ImpSetLineVersatz(MIDDLELINE, *pEdgeTrack, nVals[n]);
218 n++;
221 if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
223 aEdgeInfo.ImpSetLineVersatz(OBJ2LINE3, *pEdgeTrack, nVals[n]);
224 n++;
227 if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
229 aEdgeInfo.ImpSetLineVersatz(OBJ2LINE2, *pEdgeTrack, nVals[n]);
230 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;
238 if(bHor1)
240 aEdgeInfo.aObj1Line2.X() = nVal1;
242 else
244 aEdgeInfo.aObj1Line2.Y() = nVal1;
247 if(bHor2)
249 aEdgeInfo.aObj2Line2.X() = nVal2;
251 else
253 aEdgeInfo.aObj2Line2.Y() = nVal2;
257 ImpDirtyEdgeTrack();
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 };
269 sal_uInt16 n = 0;
271 if(eKind == SDREDGE_ORTHOLINES || eKind == SDREDGE_BEZIER)
273 if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
275 nVals[n] = aEdgeInfo.ImpGetLineVersatz(OBJ1LINE2, *pEdgeTrack);
276 n++;
279 if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
281 nVals[n] = aEdgeInfo.ImpGetLineVersatz(OBJ1LINE3, *pEdgeTrack);
282 n++;
285 if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
287 nVals[n] = aEdgeInfo.ImpGetLineVersatz(MIDDLELINE, *pEdgeTrack);
288 n++;
291 if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
293 nVals[n] = aEdgeInfo.ImpGetLineVersatz(OBJ2LINE3, *pEdgeTrack);
294 n++;
297 if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
299 nVals[n] = aEdgeInfo.ImpGetLineVersatz(OBJ2LINE2, *pEdgeTrack);
300 n++;
303 else if(eKind == SDREDGE_THREELINES)
305 bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
306 bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
308 n = 2;
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.
316 if(n != nValAnz)
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]));
336 if(n < 3)
338 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE3DELTA);
341 if(n < 2)
343 GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE2DELTA);
346 if(n < 1)
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
378 if(bEdgeTrackDirty)
380 ((SdrEdgeObj*)this)->ImpRecalcEdgeTrack();
383 return SdrTextObj::GetCurrentBoundRect();
386 const Rectangle& SdrEdgeObj::GetSnapRect() const
388 if(bEdgeTrackDirty)
390 ((SdrEdgeObj*)this)->ImpRecalcEdgeTrack();
393 return SdrTextObj::GetSnapRect();
396 void SdrEdgeObj::RecalcSnapRect()
398 maSnapRect=pEdgeTrack->GetBoundRect();
401 void SdrEdgeObj::TakeUnrotatedSnapRect(Rectangle& rRect) const
403 rRect=GetSnapRect();
406 bool SdrEdgeObj::IsNode() const
408 return true;
411 SdrGluePoint SdrEdgeObj::GetVertexGluePoint(sal_uInt16 nNum) const
413 Point aPt;
414 sal_uInt16 nPntAnz=pEdgeTrack->GetPointCount();
415 if (nPntAnz>0)
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];
420 else {
421 if ((nPntAnz & 1) ==1) {
422 aPt=(*pEdgeTrack)[nPntAnz/2];
423 } else {
424 Point aPt1((*pEdgeTrack)[nPntAnz/2-1]);
425 Point aPt2((*pEdgeTrack)[nPntAnz/2]);
426 aPt1+=aPt2;
427 aPt1.X()/=2;
428 aPt1.Y()/=2;
429 aPt=aPt1;
432 aPt-=aOfs;
434 SdrGluePoint aGP(aPt);
435 aGP.SetPercent(sal_False);
436 return aGP;
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
456 return true;
459 void SdrEdgeObj::ConnectToNode(bool bTail1, SdrObject* pObj)
461 SdrObjConnection& rCon=GetConnection(bTail1);
462 DisconnectFromNode(bTail1);
463 if (pObj!=NULL) {
464 pObj->AddListener(*this);
465 rCon.pObj=pObj;
467 // #i120437# If connection is set, reset bEdgeTrackUserDefined
468 bEdgeTrackUserDefined = false;
470 ImpDirtyEdgeTrack();
474 void SdrEdgeObj::DisconnectFromNode(bool bTail1)
476 SdrObjConnection& rCon=GetConnection(bTail1);
477 if (rCon.pObj!=NULL) {
478 rCon.pObj->RemoveListener(*this);
479 rCon.pObj=NULL;
483 SdrObject* SdrEdgeObj::GetConnectedNode(bool bTail1) const
485 SdrObject* pObj=GetConnection(bTail1).pObj;
486 if (pObj!=NULL && (pObj->GetPage()!=pPage || !pObj->IsInserted())) pObj=NULL;
487 return pObj;
490 bool SdrEdgeObj::CheckNodeConnection(bool bTail1) const
492 bool bRet = false;
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);
506 } else { // Corner
507 SdrGluePoint aPt(rCon.pObj->GetCornerGluePoint(i-nConAnz-4));
508 bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
512 return bRet;
515 void SdrEdgeObj::ImpSetTailPoint(bool bTail1, const Point& rPt)
517 sal_uInt16 nPtAnz=pEdgeTrack->GetPointCount();
518 if (nPtAnz==0) {
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; }
524 } else {
525 if (!bTail1) (*pEdgeTrack)[sal_uInt16(nPtAnz-1)]=rPt;
526 else (*pEdgeTrack)[0]=rPt;
528 ImpRecalcEdgeTrack();
529 SetRectsDirty();
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)
550 return;
553 // #i120437# also not when model locked during import, but remember
554 if(!GetModel() || GetModel()->isLocked())
556 mbSuppressed = true;
557 return;
560 // #i110649#
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.
568 else
570 if(mbSuppressed)
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
575 // set before (!)
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();
586 SetRectsDirty();
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
592 ActionChanged();
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
615 sal_uInt16 nRet=0;
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;
621 } else { // right
622 if (dyo<dyu) nRet|=SDRESC_RIGHT | SDRESC_TOP;
623 else nRet|=SDRESC_RIGHT | SDRESC_BOTTOM;
625 return nRet;
627 if (dx<dy) { // horizontal
628 if (bxMitt) return SDRESC_HORZ;
629 if (dxl<dxr) return SDRESC_LEFT;
630 else return SDRESC_RIGHT;
631 } else { // vertical
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
640 XPolygon aXP;
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);
660 Point aP3(aP2);
661 if ((bLks && rMeeting.X()>aP2.X()) || (bRts && rMeeting.X()<aP2.X())) { // around
662 if (rMeeting.Y()<aP2.Y()) {
663 aP3.Y()=rRect.Top();
664 if (rMeeting.Y()<aP3.Y()) aP3.Y()=rMeeting.Y();
665 } else {
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();
679 } else {
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);
689 #ifdef DBG_UTIL
690 if (aXP.GetPointCount()>4) {
691 OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
693 #endif
694 return aXP;
697 XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const XPolygon& rTrack0, SdrObjConnection& rCon1, SdrObjConnection& rCon2, SdrEdgeInfoRec* pInfo) const
699 Point aPt1,aPt2;
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) {
708 aPt1=rTrack0[0];
709 sal_uInt16 nSiz=rTrack0.GetPointCount();
710 nSiz--;
711 aPt2=rTrack0[nSiz];
712 } else {
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();
725 if (bCon1) {
726 if (rCon1.pObj==(SdrObject*)this)
728 // check, just in case
729 aBoundRect1=aOutRect;
731 else
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;
745 } else {
746 aBoundRect1=Rectangle(aPt1,aPt1);
747 aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
748 aBewareRect1=aBoundRect1;
750 if (bCon2) {
751 if (rCon2.pObj==(SdrObject*)this) { // check, just in case
752 aBoundRect2=aOutRect;
754 else
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;
768 } else {
769 aBoundRect2=Rectangle(aPt2,aPt2);
770 aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
771 aBewareRect2=aBoundRect2;
773 XPolygon aBestXP;
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)) {
787 aPt1=aGP1.GetPos();
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)) {
794 aPt2=aGP2.GetPos();
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) {
803 sal_uIntPtr nQual=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) {
808 aBestXP=aXP;
809 nBestQual=nQual;
810 aBestInfo=aInfo;
811 nBestAuto1=nNum1;
812 nBestAuto2=nNum2;
819 if (bAuto1) rCon1.nConId=nBestAuto1;
820 if (bAuto2) rCon2.nConId=nBestAuto2;
821 if (pInfo!=NULL) *pInfo=aBestInfo;
822 return aBestXP;
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;
843 if (bInfo) {
844 pInfo->cOrthoForm=0;
845 pInfo->nAngle1=nAngle1;
846 pInfo->nAngle2=nAngle2;
847 pInfo->nObj1Lines=1;
848 pInfo->nObj2Lines=1;
849 pInfo->nMiddleLine=0xFFFF;
851 Point aPt1(rPt1);
852 Point aPt2(rPt2);
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) {
859 XPolygon aXP(2);
860 aXP[0]=rPt1;
861 aXP[1]=rPt2;
862 if (pnQuality!=NULL) {
863 *pnQuality=std::abs(rPt1.X()-rPt2.X())+std::abs(rPt1.Y()-rPt2.Y());
865 return aXP;
866 } else if (eKind==SDREDGE_THREELINES) {
867 XPolygon aXP(4);
868 aXP[0]=rPt1;
869 aXP[1]=rPt1;
870 aXP[2]=rPt2;
871 aXP[3]=rPt2;
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());
884 *pnQuality=nQ;
886 if (bInfo) {
887 pInfo->nObj1Lines=2;
888 pInfo->nObj2Lines=2;
889 if (bHor1) {
890 aXP[1].X()+=pInfo->aObj1Line2.X();
891 } else {
892 aXP[1].Y()+=pInfo->aObj1Line2.Y();
894 if (bHor2) {
895 aXP[2].X()+=pInfo->aObj2Line2.X();
896 } else {
897 aXP[2].Y()+=pInfo->aObj2Line2.Y();
900 return aXP;
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;
911 } else {
912 // otherwise the center point of the empty space
913 if (aC1.X()<aC2.X()) {
914 aMeeting.X()=(aBewareRect1.Right()+aBewareRect2.Left()+1)/2;
915 } else {
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;
924 } else {
925 // otherwise the center point of the empty space
926 if (aC1.Y()<aC2.Y()) {
927 aMeeting.Y()=(aBewareRect1.Bottom()+aBewareRect2.Top()+1)/2;
928 } else {
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())) {
954 aMeeting.X()=nXMin;
956 if (bRts1 && (bY1Ok || aBewareRect1.Right()>aBewareRect2.Left()) && (bY2Ok || aBewareRect2.Right()>aBewareRect1.Left())) {
957 aMeeting.X()=nXMax;
959 if (bObn1 && (bX1Ok || aBewareRect1.Top()<aBewareRect2.Bottom()) && (bX2Ok || aBewareRect2.Top()<aBewareRect1.Bottom())) {
960 aMeeting.Y()=nYMin;
962 if (bUnt1 && (bX1Ok || aBewareRect1.Bottom()>aBewareRect2.Top()) && (bX2Ok || aBewareRect2.Bottom()>aBewareRect1.Top())) {
963 aMeeting.Y()=nYMax;
965 } else if (nMainCase==2) {
966 // case 2:
967 if (bHor1) { // both horizontal
968 /* 9 sub-cases:
969 (legend: line exits to the left (-|), right (|-))
971 2.1: Facing; overlap only on y axis
972 * * *
973 |--| *
974 * * *
976 2.2, 2.3: Facing, offset vertically; no overlap on either
977 axis
978 |- * * * * *
979 * -| * * -| *
980 * * * , * * *
982 2.4, 2.5: One below the other; overlap only on y axis
983 * |- * * * *
984 * -| * * -| *
985 * * * , * |- *
987 2.6, 2.7: Not facing, offset vertically; no overlap on either
988 axis
989 * * |- * * *
990 * -| * * -| *
991 * * * , * * |-
993 2.8: Not facing; overlap only on y axis
994 * * *
995 * -| |-
996 * * *
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()) {
1016 /* Cases 2.8, 2.9:
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());
1036 if (bObenLang) {
1037 aMeeting.Y()=nYMin;
1038 } else {
1039 aMeeting.Y()=nYMax;
1041 if (bCase29) {
1042 // now make sure that the surrounded object
1043 // isn't traversed
1044 if ((aBewR1.Center().Y()<aBewR2.Center().Y()) != bObenLang) {
1045 aMeeting.X()=aBewR2.Right();
1046 } else {
1047 aMeeting.X()=aBewR1.Left();
1050 } else {
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;
1062 } else {
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()) {
1078 /* Cases 2.8, 2.9
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());
1097 if (bLinksLang) {
1098 aMeeting.X()=nXMin;
1099 } else {
1100 aMeeting.X()=nXMax;
1102 if (bCase29) {
1103 // now make sure that the surrounded object
1104 // isn't traversed
1105 if ((aBewR1.Center().X()<aBewR2.Center().X()) != bLinksLang) {
1106 aMeeting.Y()=aBewR2.Bottom();
1107 } else {
1108 aMeeting.Y()=aBewR1.Top();
1111 } else {
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;
1123 } else {
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
1133 /* legend:
1134 The line exits to the:
1135 -| left
1137 |- right
1139 _|_ top
1141 T bottom
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: * . * . * * . * . *
1181 // case 3.2
1182 Rectangle aTmpR1(aBewareRect1);
1183 Rectangle aTmpR2(aBewareRect2);
1184 if (bBewareOverlap) {
1185 // overlapping BewareRects: use BoundRects for checking for case 3.2
1186 aTmpR1=aBoundRect1;
1187 aTmpR2=aBoundRect2;
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
1194 if (bHor1) {
1195 aMeeting.X()=aPt2.X();
1196 aMeeting.Y()=aPt1.Y();
1197 } else {
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())))) {
1212 // case 3.3
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();
1225 if (bInfo) {
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);
1241 if (bInfo) {
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++;
1247 } else {
1248 if (pInfo->nObj1Lines>pInfo->nObj2Lines) {
1249 pInfo->nObj2Lines++;
1250 pInfo->nMiddleLine=nXP1Anz-1;
1251 } else {
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--;
1263 while (nNum>0) {
1264 nNum--;
1265 aXP1.Insert(XPOLY_APPEND,aXP2[nNum],XPOLY_NORMAL);
1267 sal_uInt16 nPntAnz=aXP1.GetPointCount();
1268 char cForm=0;
1269 if (bInfo || pnQuality!=NULL) {
1270 cForm='?';
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';
1275 else cForm='Z';
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.
1280 Point aP1(aXP1[1]);
1281 Point aP2(aXP1[2]);
1282 Point aP3(aXP1[3]);
1283 Point aP4(aXP1[4]);
1284 if (aP1.Y()==aP2.Y()) { // else both lines are horizontal
1285 if ((aP1.X()<aP2.X())==(aP3.X()<aP4.X())) cForm='S';
1286 else cForm='C';
1287 } else { // else both lines are vertical
1288 if ((aP1.Y()<aP2.Y())==(aP3.Y()<aP4.Y())) cForm='S';
1289 else cForm='C';
1291 } else cForm='4'; // else is case 3 with 5 lines
1292 } else cForm='?'; //
1293 // more shapes:
1294 if (bInfo) {
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;
1301 } else {
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;
1320 nQual0=nQual;
1321 aPt0=aPt1b;
1324 sal_uInt16 nTmp=nPntAnz;
1325 if (cForm=='Z') {
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;
1332 n1+=n3;
1333 n3=n2/4;
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;
1340 if (nTmp>=3) {
1341 nQual0=nQual;
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++;
1371 } else {
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++;
1393 aPt0=aPt1b;
1395 if (nPntAnz<=1) nIntersections++;
1396 nQual0=nQual;
1397 nQual+=(sal_uIntPtr)nIntersections*0x10000000;
1398 if (nQual<nQual0 || nIntersections>15) bOverflow = true;
1400 if (bOverflow || nQual==0xFFFFFFFF) nQual=0xFFFFFFFE;
1401 *pnQuality=nQual;
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();
1409 } else {
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();
1419 } else {
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();
1429 } else {
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();
1439 } else {
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();
1449 } else {
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);
1467 Point aPt3(*pPt2);
1468 aXP1.Insert(2,aPt3,XPOLY_CONTROL);
1469 nPntAnz=aXP1.GetPointCount();
1470 pPt2=&aXP1[1];
1471 pPt3=&aXP1[nPntAnz-2];
1472 pPt2->X()-=dx1/3;
1473 pPt2->Y()-=dy1/3;
1474 pPt3->X()-=dx2/3;
1475 pPt3->Y()-=dy2/3;
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);
1481 // distance x1.5
1482 pPt2->X()+=dx1/2;
1483 pPt2->Y()+=dy1/2;
1484 pPt3->X()+=dx2/2;
1485 pPt3->Y()+=dy2/2;
1486 if (nPntAnz==5) {
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;
1503 if (nPntAnz==6) {
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();
1510 aXP1[3].X()-=dx/2;
1511 aXP1[3].Y()-=dy/2;
1512 aXP1.SetFlags(3,XPOLY_SYMMTR);
1513 aXP1.Remove(4,1); // because it's identical with aXP1[3]
1517 return aXP1;
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") _
1534 __| _|
1536 4 lines #1 _| #2 | | #3 |_ #4 | |
1537 _| _| _| _|
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 _
1542 _| _| _| _| _| |_
1543 _ _ _
1544 #5 |_ #6 |_ #7 _| | #8 ____
1545 _| _| _| |_ _|
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:
1553 CL O1 O2 Note
1554 "I": n 0 0
1555 "L": n 0 0
1556 "U": n 0-1 0-1
1557 "Z": y 0 0
1558 4.2: y 0 1 = U+1, respectively 1+U
1559 4.4: n 0-2 0-2 = Z+1
1560 "4": y 0 2 = Z+2
1561 "S": y 1 1 = 1+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;
1578 return;
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
1591 if (bDataChg ||
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
1601 ActionChanged();
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)
1634 if( this == &rObj )
1635 return *this;
1636 SdrTextObj::operator=(rObj);
1637 *pEdgeTrack =*rObj.pEdgeTrack;
1638 bEdgeTrackDirty=rObj.bEdgeTrackDirty;
1639 aCon1 =rObj.aCon1;
1640 aCon2 =rObj.aCon2;
1641 aCon1.pObj=NULL;
1642 aCon2.pObj=NULL;
1643 aEdgeInfo=rObj.aEdgeInfo;
1644 return *this;
1647 void SdrEdgeObj::TakeObjNameSingul(XubString& rName) const
1649 rName=ImpGetResStr(STR_ObjNameSingulEDGE);
1651 String aName( GetName() );
1652 if(aName.Len())
1654 rName += sal_Unicode(' ');
1655 rName += sal_Unicode('\'');
1656 rName += aName;
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();
1675 if(pEdgeTrack)
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;
1690 else
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());
1721 if(nPntAnz)
1723 nHdlAnz = 2L;
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))
1735 nHdlAnz++;
1737 if(GetConnectedNode(sal_False))
1738 nHdlAnz++;
1742 return nHdlAnz;
1745 SdrHdl* SdrEdgeObj::GetHdl(sal_uInt32 nHdlNum) const
1747 SdrHdl* pHdl=NULL;
1748 sal_uInt32 nPntAnz(pEdgeTrack->GetPointCount());
1749 if (nPntAnz!=0) {
1750 if (nHdlNum==0) {
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);
1756 } else {
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);
1763 sal_Int32 nPt(0L);
1764 pHdl=new ImpEdgeHdl(Point(),HDL_POLY);
1765 if (nNum<nO1) {
1766 nPt=nNum+1L;
1767 if (nNum==0) ((ImpEdgeHdl*)pHdl)->SetLineCode(OBJ1LINE2);
1768 if (nNum==1) ((ImpEdgeHdl*)pHdl)->SetLineCode(OBJ1LINE3);
1769 } else {
1770 nNum=nNum-nO1;
1771 if (nNum<nO2) {
1772 nPt=nPntAnz-3-nNum;
1773 if (nNum==0) ((ImpEdgeHdl*)pHdl)->SetLineCode(OBJ2LINE2);
1774 if (nNum==1) ((ImpEdgeHdl*)pHdl)->SetLineCode(OBJ2LINE3);
1775 } else {
1776 nNum=nNum-nO2;
1777 if (nNum<nM) {
1778 nPt=aEdgeInfo.nMiddleLine;
1779 ((ImpEdgeHdl*)pHdl)->SetLineCode(MIDDLELINE);
1783 if (nPt>0) {
1784 Point aPos((*pEdgeTrack)[(sal_uInt16)nPt]);
1785 aPos+=(*pEdgeTrack)[(sal_uInt16)nPt+1];
1786 aPos.X()/=2;
1787 aPos.Y()/=2;
1788 pHdl->SetPos(aPos);
1789 } else {
1790 delete pHdl;
1791 pHdl=NULL;
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);
1802 if (pHdl!=NULL) {
1803 pHdl->SetPointNum(nHdlNum);
1806 return pHdl;
1809 ////////////////////////////////////////////////////////////////////////////////////////////////////
1811 bool SdrEdgeObj::hasSpecialDrag() const
1813 return true;
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));
1825 return pRetval;
1828 bool SdrEdgeObj::beginSpecialDrag(SdrDragStat& rDrag) const
1830 if(!rDrag.GetHdl())
1831 return false;
1833 rDrag.SetEndDragChangesAttributes(true);
1835 if(rDrag.GetHdl()->GetPointNum() < 2)
1837 rDrag.SetNoSnap(true);
1840 return 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);
1868 // clear connection
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
1877 // sets pObj hard
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());
1891 if(pEdgeTrack)
1893 // change pEdgeTrack to modified position
1894 if(bDragA)
1896 (*pEdgeTrack)[0] = aPointNow;
1898 else
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();
1911 else
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;
1931 SetRectsDirty();
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();
1940 return true;
1943 String SdrEdgeObj::getSpecialDragComment(const SdrDragStat& rDrag) const
1945 const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
1947 if(bCreateComment)
1949 return String();
1951 else
1953 OUString aStr;
1954 ImpTakeDescriptionStr(STR_DragEdgeTail, aStr);
1956 return aStr;
1960 ////////////////////////////////////////////////////////////////////////////////////////////////////
1962 basegfx::B2DPolygon SdrEdgeObj::ImplAddConnectorOverlay(SdrDragMethod& rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
1964 basegfx::B2DPolygon aResult;
1966 if(bDetail)
1968 SdrObjConnection aMyCon1(aCon1);
1969 SdrObjConnection aMyCon2(aCon2);
1971 if (bTail1)
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());
1978 if (bTail2)
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();
1993 else
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();
2004 if (bTail1)
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());
2011 if (bTail2)
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()));
2022 return aResult;
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);
2036 return sal_True;
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;
2052 return sal_True;
2055 bool SdrEdgeObj::EndCreate(SdrDragStat& rDragStat, SdrCreateCmd eCmd)
2057 bool bOk=(eCmd==SDRCREATE_FORCEEND || rDragStat.GetPointAnz()>=2);
2058 if (bOk) {
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
2066 SetRectsDirty();
2067 return bOk;
2070 bool SdrEdgeObj::BckCreate(SdrDragStat& rDragStat)
2072 if (rDragStat.GetView()!=NULL) {
2073 rDragStat.GetView()->HideConnectMarker();
2075 return sal_False;
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());
2089 return aRetval;
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)
2099 rCon.ResetVars();
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();
2115 bool bFnd = false;
2116 SdrObjConnection aTestCon;
2117 SdrObjConnection aBestCon;
2119 while (no>0 && !bFnd) {
2120 // issue: group objects on different layers return LayerID=0!
2121 no--;
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
2125 pObj->IsNode())
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;
2145 bool bOk = false;
2146 Point aConPos;
2147 sal_uInt16 nConNum=i;
2148 if (bUser) {
2149 const SdrGluePoint& rGP=(*pGPL)[nConNum];
2150 aConPos=rGP.GetAbsolutePos(*pObj);
2151 nConNum=rGP.GetId();
2152 bOk = true;
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);
2158 bOk = true;
2159 } else i+=3;
2160 } else if (bCorner && !bUserFnd) {
2161 nConNum-=nConAnz+4;
2162 if (rPV.GetView().IsAutoCornerConnectors()) {
2163 SdrGluePoint aPt(pObj->GetCornerGluePoint(nConNum));
2164 aConPos=aPt.GetAbsolutePos(*pObj);
2165 bOk = true;
2166 } else i+=3;
2168 else if (bCenter && !bUserFnd && !bEdge)
2170 // Suppress default connect at object center
2171 if(!pThis || !pThis->GetSuppressDefaultConnect())
2173 // not the edges!
2174 nConNum=0;
2175 aConPos=aObjBound.Center();
2176 bOk = true;
2179 if (bOk && aMouseRect.IsInside(aConPos)) {
2180 if (bUser) bUserFnd = true;
2181 bFnd = 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) {
2184 nBestDist=nDist;
2185 aTestCon.pObj=pObj;
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)
2195 if(!bFnd &&
2196 !bEdge &&
2197 SdrObjectPrimitiveHit(*pObj, rPt, nBoundHitTol, rPV, &rVisLayer, false))
2199 // Suppress default connect at object inside bound
2200 if(!pThis || !pThis->GetSuppressDefaultConnect())
2202 bFnd = true;
2203 aTestCon.pObj=pObj;
2204 aTestCon.bBestConn=sal_True;
2207 if (bFnd) {
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);
2219 rCon=aTestCon;
2220 return bFnd;
2223 void SdrEdgeObj::NbcSetSnapRect(const Rectangle& rRect)
2225 const Rectangle aOld(GetSnapRect());
2227 if(aOld != rRect)
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 (!)
2233 aRect = rRect;
2234 maSnapRect = rRect;
2236 else
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);
2284 else
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);
2315 else
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);
2346 else
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);
2373 if(bAddText)
2375 pRet = ImpConvertAddText(pRet, bBezier);
2378 return pRet;
2381 sal_uInt32 SdrEdgeObj::GetSnapPointCount() const
2383 return 2L;
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
2396 return sal_False;
2399 sal_uInt32 SdrEdgeObj::GetPointCount() const
2401 return 0L;
2404 Point SdrEdgeObj::GetPoint(sal_uInt32 i) const
2406 ((SdrEdgeObj*)this)->ImpUndirtyEdgeTrack();
2407 sal_uInt16 nAnz=pEdgeTrack->GetPointCount();
2408 if (0L == i)
2409 return (*pEdgeTrack)[0];
2410 else
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();
2419 if (0L == i)
2420 (*pEdgeTrack)[0]=rPnt;
2421 if (1L == i)
2422 (*pEdgeTrack)[nAnz-1]=rPnt;
2423 SetEdgeTrackDirty();
2424 SetRectsDirty();
2427 SdrEdgeObjGeoData::SdrEdgeObjGeoData()
2429 pEdgeTrack=new XPolygon;
2432 SdrEdgeObjGeoData::~SdrEdgeObjGeoData()
2434 delete pEdgeTrack;
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;
2446 rEGeo.aCon1 =aCon1;
2447 rEGeo.aCon2 =aCon2;
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);
2460 aCon1=rEGeo.aCon1;
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);
2465 aCon2=rEGeo.aCon2;
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;
2479 if(bTail)
2481 return rTrack0[0];
2483 else
2485 const sal_uInt16 nSiz = rTrack0.GetPointCount() - 1;
2486 return rTrack0[nSiz];
2489 else
2491 if(bTail)
2492 return aOutRect.TopLeft();
2493 else
2494 return aOutRect.BottomRight();
2499 void SdrEdgeObj::SetTailPoint( sal_Bool bTail, const Point& rPt )
2501 ImpSetTailPoint( bTail, rPt );
2502 SetChanged();
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 );
2520 if( nIndex > 3 )
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) )
2528 return;
2530 else if( nIndex < 0 )
2532 nIndex = 0;
2535 rConn1.SetConnectorId( (sal_uInt16)nIndex );
2537 SetChanged();
2538 SetRectsDirty();
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 );
2547 sal_Int32 nId = -1;
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
2554 return nId;
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
2584 if(bEdgeTrackDirty)
2586 const_cast< SdrEdgeObj* >(this)->ImpRecalcEdgeTrack();
2589 if(pEdgeTrack)
2591 return pEdgeTrack->getB2DPolygon();
2593 else
2595 return ::basegfx::B2DPolygon();
2599 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */