LibreOffice Module svx (master)  1
svdoedge.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "svddrgm1.hxx"
21 #include <svx/dialmgr.hxx>
22 #include <svx/strings.hrc>
23 
27 #include <editeng/eeitem.hxx>
28 #include <svl/hint.hxx>
29 #include <svl/style.hxx>
30 
33 #include <svx/sdrhittesthelper.hxx>
34 #include <svx/svddrag.hxx>
35 #include <svx/svddrgv.hxx>
36 #include <svx/svdetc.hxx>
37 #include <svx/svdhdl.hxx>
38 #include <svx/svdmodel.hxx>
39 #include <svx/svdoedge.hxx>
40 #include <svx/svdopath.hxx>
41 #include <svx/svdpage.hxx>
42 #include <svx/svdpagv.hxx>
43 #include <svx/svdpool.hxx>
44 #include <svx/svdtrans.hxx>
45 #include <svx/svdview.hxx>
46 #include <svx/sxekitm.hxx>
47 #include <svx/sxelditm.hxx>
48 #include <svx/sxenditm.hxx>
49 #include <svx/xpoly.hxx>
50 #include <svx/xpool.hxx>
51 #include <vcl/ptrstyle.hxx>
52 
54 {
55  pObj=nullptr;
56  nConId=0;
57  bBestConn=true;
58  bBestVertex=true;
59  bAutoVertex=false;
60  bAutoCorner=false;
61 }
62 
64 {
65  bool bRet = false;
66  if (pObj!=nullptr) { // one object has to be docked already!
67  if (bAutoVertex) {
69  bRet = true;
70  } else if (bAutoCorner) {
72  bRet = true;
73  } else {
74  const SdrGluePointList* pGPL=pObj->GetGluePointList();
75  if (pGPL!=nullptr) {
76  sal_uInt16 nNum=pGPL->FindGluePoint(nConId);
77  if (nNum!=SDRGLUEPOINT_NOTFOUND) {
78  rGP=(*pGPL)[nNum];
79  bRet = true;
80  }
81  }
82  }
83  }
84  if (bRet) {
85  Point aPt(rGP.GetAbsolutePos(*pObj));
86  aPt+=aObjOfs;
87  rGP.SetPos(aPt);
88  }
89  return bRet;
90 }
91 
93 {
94  switch (eLineCode) {
100  } // switch
101  return aMiddleLine;
102 }
103 
104 sal_uInt16 SdrEdgeInfoRec::ImpGetPolyIdx(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
105 {
106  switch (eLineCode) {
107  case SdrEdgeLineCode::Obj1Line2 : return 1;
108  case SdrEdgeLineCode::Obj1Line3 : return 2;
109  case SdrEdgeLineCode::Obj2Line2 : return rXP.GetPointCount()-3;
110  case SdrEdgeLineCode::Obj2Line3 : return rXP.GetPointCount()-4;
112  } // switch
113  return 0;
114 }
115 
116 bool SdrEdgeInfoRec::ImpIsHorzLine(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
117 {
118  sal_uInt16 nIdx=ImpGetPolyIdx(eLineCode,rXP);
119  bool bHorz=nAngle1==0 || nAngle1==18000;
120  if (eLineCode==SdrEdgeLineCode::Obj2Line2 || eLineCode==SdrEdgeLineCode::Obj2Line3) {
121  nIdx=rXP.GetPointCount()-nIdx;
122  bHorz=nAngle2==0 || nAngle2==18000;
123  }
124  if ((nIdx & 1)==1) bHorz=!bHorz;
125  return bHorz;
126 }
127 
128 void SdrEdgeInfoRec::ImpSetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon& rXP, long nVal)
129 {
130  Point& rPt=ImpGetLineOffsetPoint(eLineCode);
131  if (ImpIsHorzLine(eLineCode,rXP)) rPt.setY(nVal );
132  else rPt.setX(nVal );
133 }
134 
136 {
137  const Point& rPt = const_cast<SdrEdgeInfoRec*>(this)->ImpGetLineOffsetPoint(eLineCode);
138  if (ImpIsHorzLine(eLineCode,rXP))
139  return rPt.Y();
140  else
141  return rPt.X();
142 }
143 
144 
145 // BaseProperties section
146 
147 std::unique_ptr<sdr::properties::BaseProperties> SdrEdgeObj::CreateObjectSpecificProperties()
148 {
149  return std::make_unique<sdr::properties::ConnectorProperties>(*this);
150 }
151 
152 
153 // DrawContact section
154 
155 std::unique_ptr<sdr::contact::ViewContact> SdrEdgeObj::CreateObjectSpecificViewContact()
156 {
157  return std::make_unique<sdr::contact::ViewContactOfSdrEdgeObj>(*this);
158 }
159 
160 
162 : SdrTextObj(rSdrModel),
163  nNotifyingCount(0),
164  bEdgeTrackDirty(false),
165  bEdgeTrackUserDefined(false),
166  // Default is to allow default connects
167  mbSuppressDefaultConnect(false),
168  mbBoundRectCalculationRunning(false),
169  mbSuppressed(false)
170 {
171  bClosedObj=false;
172  bIsEdge=true;
173  pEdgeTrack.reset(new XPolygon);
174 }
175 
177 {
180 }
181 
182 void SdrEdgeObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
183 {
184  // call parent
185  SdrTextObj::handlePageChange(pOldPage, pNewPage);
186 
187  if(nullptr != GetConnection(true).GetObject() || nullptr != GetConnection(false).GetObject())
188  {
189  // check broadcasters; when we are not inserted we do not need broadcasters
190  // TTTT not yet added, but keep hint to do this here
191  // mpCon1->ownerPageChange();
192  // mpCon2->ownerPageChange();
193  }
194 }
195 
197 {
198  const SfxItemSet& rSet = GetObjectItemSet();
199  SdrEdgeKind eKind = rSet.Get(SDRATTR_EDGEKIND).GetValue();
200  sal_Int32 nVal1 = rSet.Get(SDRATTR_EDGELINE1DELTA).GetValue();
201  sal_Int32 nVal2 = rSet.Get(SDRATTR_EDGELINE2DELTA).GetValue();
202  sal_Int32 nVal3 = rSet.Get(SDRATTR_EDGELINE3DELTA).GetValue();
203 
204  if(eKind == SdrEdgeKind::OrthoLines || eKind == SdrEdgeKind::Bezier)
205  {
206  sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
207  sal_uInt16 n = 0;
208 
209  if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
210  {
212  n++;
213  }
214 
215  if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
216  {
218  n++;
219  }
220 
221  if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
222  {
224  n++;
225  }
226 
227  if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
228  {
230  n++;
231  }
232 
233  if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
234  {
236  n++;
237  }
238  }
239  else if(eKind == SdrEdgeKind::ThreeLines)
240  {
241  bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
242  bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
243 
244  if(bHor1)
245  {
246  aEdgeInfo.aObj1Line2.setX( nVal1 );
247  }
248  else
249  {
250  aEdgeInfo.aObj1Line2.setY( nVal1 );
251  }
252 
253  if(bHor2)
254  {
255  aEdgeInfo.aObj2Line2.setX( nVal2 );
256  }
257  else
258  {
259  aEdgeInfo.aObj2Line2.setY( nVal2 );
260  }
261  }
262 
264 }
265 
267 {
268  const SfxItemSet& rSet = GetObjectItemSet();
269  SdrEdgeKind eKind = rSet.Get(SDRATTR_EDGEKIND).GetValue();
270  sal_Int32 nValCnt = rSet.Get(SDRATTR_EDGELINEDELTACOUNT).GetValue();
271  sal_Int32 nVal1 = rSet.Get(SDRATTR_EDGELINE1DELTA).GetValue();
272  sal_Int32 nVal2 = rSet.Get(SDRATTR_EDGELINE2DELTA).GetValue();
273  sal_Int32 nVal3 = rSet.Get(SDRATTR_EDGELINE3DELTA).GetValue();
274  sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
275  sal_uInt16 n = 0;
276 
277  if(eKind == SdrEdgeKind::OrthoLines || eKind == SdrEdgeKind::Bezier)
278  {
279  if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
280  {
282  n++;
283  }
284 
285  if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
286  {
288  n++;
289  }
290 
291  if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
292  {
294  n++;
295  }
296 
297  if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
298  {
300  n++;
301  }
302 
303  if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
304  {
306  n++;
307  }
308  }
309  else if(eKind == SdrEdgeKind::ThreeLines)
310  {
311  bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
312  bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
313 
314  n = 2;
315  nVals[0] = bHor1 ? aEdgeInfo.aObj1Line2.X() : aEdgeInfo.aObj1Line2.Y();
316  nVals[1] = bHor2 ? aEdgeInfo.aObj2Line2.X() : aEdgeInfo.aObj2Line2.Y();
317  }
318 
319  if(n != nValCnt || nVals[0] != nVal1 || nVals[1] != nVal2 || nVals[2] != nVal3)
320  {
321  // Here no more notifying is necessary, just local changes are OK.
322  if(n != nValCnt)
323  {
325  }
326 
327  if(nVals[0] != nVal1)
328  {
330  }
331 
332  if(nVals[1] != nVal2)
333  {
335  }
336 
337  if(nVals[2] != nVal3)
338  {
340  }
341 
342  if(n < 3)
343  {
345  }
346 
347  if(n < 2)
348  {
350  }
351 
352  if(n < 1)
353  {
355  }
356  }
357 }
358 
360 {
361  // #i54102# allow rotation, mirror and shear
362  rInfo.bRotateFreeAllowed = true;
363  rInfo.bRotate90Allowed = true;
364  rInfo.bMirrorFreeAllowed = true;
365  rInfo.bMirror45Allowed = true;
366  rInfo.bMirror90Allowed = true;
367  rInfo.bTransparenceAllowed = false;
368  rInfo.bShearAllowed = true;
369  rInfo.bEdgeRadiusAllowed = false;
370  bool bCanConv=!HasText() || ImpCanConvTextToCurve();
371  rInfo.bCanConvToPath=bCanConv;
372  rInfo.bCanConvToPoly=bCanConv;
374 }
375 
377 {
378  return sal_uInt16(OBJ_EDGE);
379 }
380 
382 {
383  if(bEdgeTrackDirty)
384  {
385  const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
386  }
387 
389 }
390 
392 {
393  if(bEdgeTrackDirty)
394  {
395  const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
396  }
397 
398  return SdrTextObj::GetSnapRect();
399 }
400 
402 {
403  maSnapRect=pEdgeTrack->GetBoundRect();
404 }
405 
407 {
408  rRect=GetSnapRect();
409 }
410 
412 {
413  Point aPt;
414  sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
415  if (nPointCount>0)
416  {
417  Point aOfs = GetSnapRect().Center();
418  if (nNum==2 && GetConnectedNode(true)==nullptr) aPt=(*pEdgeTrack)[0];
419  else if (nNum==3 && GetConnectedNode(false)==nullptr) aPt=(*pEdgeTrack)[nPointCount-1];
420  else {
421  if ((nPointCount & 1) ==1) {
422  aPt=(*pEdgeTrack)[nPointCount/2];
423  } else {
424  Point aPt1((*pEdgeTrack)[nPointCount/2-1]);
425  Point aPt2((*pEdgeTrack)[nPointCount/2]);
426  aPt1+=aPt2;
427  aPt1.setX( aPt1.X() / 2 );
428  aPt1.setY( aPt1.Y() / 2 );
429  aPt=aPt1;
430  }
431  }
432  aPt-=aOfs;
433  }
434  SdrGluePoint aGP(aPt);
435  aGP.SetPercent(false);
436  return aGP;
437 }
438 
440 {
441  return GetVertexGluePoint(nNum);
442 }
443 
445 {
446  return nullptr; // no user defined glue points for connectors
447 }
448 
450 {
451  return nullptr; // no user defined glue points for connectors
452 }
453 
454 void SdrEdgeObj::ConnectToNode(bool bTail1, SdrObject* pObj)
455 {
456  SdrObjConnection& rCon=GetConnection(bTail1);
457  DisconnectFromNode(bTail1);
458  if (pObj!=nullptr) {
459  pObj->AddListener(*this);
460  rCon.pObj=pObj;
461 
462  // #i120437# If connection is set, reset bEdgeTrackUserDefined
463  bEdgeTrackUserDefined = false;
464 
466  }
467 }
468 
470 {
471  SdrObjConnection& rCon=GetConnection(bTail1);
472  if (rCon.pObj!=nullptr) {
473  rCon.pObj->RemoveListener(*this);
474  rCon.pObj=nullptr;
475  }
476 }
477 
479 {
480  SdrObject* pObj(GetConnection(bTail1).pObj);
481 
482  if(nullptr != pObj
483  && (pObj->getSdrPageFromSdrObject() != getSdrPageFromSdrObject() || !pObj->IsInserted()))
484  {
485  pObj = nullptr;
486  }
487 
488  return pObj;
489 }
490 
491 bool SdrEdgeObj::CheckNodeConnection(bool bTail1) const
492 {
493  bool bRet = false;
494  const SdrObjConnection& rCon=GetConnection(bTail1);
495  sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
496 
497  if(nullptr != rCon.pObj && rCon.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject() && 0 != nPointCount)
498  {
499  const SdrGluePointList* pGPL=rCon.pObj->GetGluePointList();
500  sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
501  sal_uInt16 nGesAnz=nGluePointCnt+8;
502  Point aTail(bTail1 ? (*pEdgeTrack)[0] : (*pEdgeTrack)[sal_uInt16(nPointCount-1)]);
503  for (sal_uInt16 i=0; i<nGesAnz && !bRet; i++) {
504  if (i<nGluePointCnt) { // UserDefined
505  bRet=aTail==(*pGPL)[i].GetAbsolutePos(*rCon.pObj);
506  } else if (i<nGluePointCnt+4) { // Vertex
507  SdrGluePoint aPt(rCon.pObj->GetVertexGluePoint(i-nGluePointCnt));
508  bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
509  } else { // Corner
510  SdrGluePoint aPt(rCon.pObj->GetCornerGluePoint(i-nGluePointCnt-4));
511  bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
512  }
513  }
514  }
515  return bRet;
516 }
517 
518 void SdrEdgeObj::ImpSetTailPoint(bool bTail1, const Point& rPt)
519 {
520  sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
521  if (nPointCount==0) {
522  (*pEdgeTrack)[0]=rPt;
523  (*pEdgeTrack)[1]=rPt;
524  } else if (nPointCount==1) {
525  if (!bTail1) (*pEdgeTrack)[1]=rPt;
526  else { (*pEdgeTrack)[1]=(*pEdgeTrack)[0]; (*pEdgeTrack)[0]=rPt; }
527  } else {
528  if (!bTail1) (*pEdgeTrack)[sal_uInt16(nPointCount-1)]=rPt;
529  else (*pEdgeTrack)[0]=rPt;
530  }
532  SetRectsDirty();
533 }
534 
536 {
537  if ( !bEdgeTrackUserDefined || !getSdrModelFromSdrObject().isLocked() )
538  bEdgeTrackDirty = true;
539 }
540 
542 {
543  if (bEdgeTrackDirty && getSdrModelFromSdrObject().isLocked())
544  {
546  }
547 }
548 
550 {
551  // #i120437# if bEdgeTrackUserDefined, do not recalculate
553  {
554  return;
555  }
556 
557  // #i120437# also not when model locked during import, but remember
558  if(getSdrModelFromSdrObject().isLocked())
559  {
560  mbSuppressed = true;
561  return;
562  }
563 
564  // #i110649#
566  {
567  // This object is involved into another ImpRecalcEdgeTrack() call
568  // from another SdrEdgeObj. Do not calculate again to avoid loop.
569  // Also, do not change bEdgeTrackDirty so that it gets recalculated
570  // later at the first non-looping call.
571  }
572  else
573  {
574  // To not run in a depth loop, use a coloring algorithm on
575  // SdrEdgeObj BoundRect calculations
577 
578  if(mbSuppressed)
579  {
580  // #i123048# If layouting was ever suppressed, it needs to be done once
581  // and the attr need to be set at EdgeInfo, else these attr *will be lost*
582  // in the following call to ImpSetEdgeInfoToAttr() since they were never
583  // set before (!)
586  mbSuppressed = false;
587  }
588 
589  tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
590  SetRectsDirty();
592  ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
593  bEdgeTrackDirty=false;
594 
595  // Only redraw here, no object change
596  ActionChanged();
597 
599 
601  }
602 }
603 
605 {
606  if (pObj==nullptr) return SdrEscapeDirection::ALL;
607  tools::Rectangle aR(pObj->GetSnapRect());
608  long dxl=rPt.X()-aR.Left();
609  long dyo=rPt.Y()-aR.Top();
610  long dxr=aR.Right()-rPt.X();
611  long dyu=aR.Bottom()-rPt.Y();
612  bool bxMitt=std::abs(dxl-dxr)<2;
613  bool byMitt=std::abs(dyo-dyu)<2;
614  long dx=std::min(dxl,dxr);
615  long dy=std::min(dyo,dyu);
616  bool bDiag=std::abs(dx-dy)<2;
617  if (bxMitt && byMitt) return SdrEscapeDirection::ALL; // in the center
618  if (bDiag) { // diagonally
620  if (byMitt) nRet|=SdrEscapeDirection::VERT;
621  if (bxMitt) nRet|=SdrEscapeDirection::HORZ;
622  if (dxl<dxr) { // left
625  } else { // right
628  }
629  return nRet;
630  }
631  if (dx<dy) { // horizontal
632  if (bxMitt) return SdrEscapeDirection::HORZ;
633  if (dxl<dxr) return SdrEscapeDirection::LEFT;
634  else return SdrEscapeDirection::RIGHT;
635  } else { // vertical
636  if (byMitt) return SdrEscapeDirection::VERT;
637  if (dyo<dyu) return SdrEscapeDirection::TOP;
638  else return SdrEscapeDirection::BOTTOM;
639  }
640 }
641 
642 XPolygon SdrEdgeObj::ImpCalcObjToCenter(const Point& rStPt, long nEscAngle, const tools::Rectangle& rRect, const Point& rMeeting)
643 {
644  XPolygon aXP;
645  aXP.Insert(XPOLY_APPEND,rStPt,PolyFlags::Normal);
646  bool bRts=nEscAngle==0;
647  bool bObn=nEscAngle==9000;
648  bool bLks=nEscAngle==18000;
649  bool bUnt=nEscAngle==27000;
650 
651  Point aP1(rStPt); // mandatory difference first,...
652  if (bLks) aP1.setX(rRect.Left() );
653  if (bRts) aP1.setX(rRect.Right() );
654  if (bObn) aP1.setY(rRect.Top() );
655  if (bUnt) aP1.setY(rRect.Bottom() );
656 
657  Point aP2(aP1); // ...now increase to Meeting height, if necessary
658  if (bLks && rMeeting.X()<=aP2.X()) aP2.setX(rMeeting.X() );
659  if (bRts && rMeeting.X()>=aP2.X()) aP2.setX(rMeeting.X() );
660  if (bObn && rMeeting.Y()<=aP2.Y()) aP2.setY(rMeeting.Y() );
661  if (bUnt && rMeeting.Y()>=aP2.Y()) aP2.setY(rMeeting.Y() );
662  aXP.Insert(XPOLY_APPEND,aP2,PolyFlags::Normal);
663 
664  Point aP3(aP2);
665  if ((bLks && rMeeting.X()>aP2.X()) || (bRts && rMeeting.X()<aP2.X())) { // around
666  if (rMeeting.Y()<aP2.Y()) {
667  aP3.setY(rRect.Top() );
668  if (rMeeting.Y()<aP3.Y()) aP3.setY(rMeeting.Y() );
669  } else {
670  aP3.setY(rRect.Bottom() );
671  if (rMeeting.Y()>aP3.Y()) aP3.setY(rMeeting.Y() );
672  }
673  aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
674  if (aP3.Y()!=rMeeting.Y()) {
675  aP3.setX(rMeeting.X() );
676  aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
677  }
678  }
679  if ((bObn && rMeeting.Y()>aP2.Y()) || (bUnt && rMeeting.Y()<aP2.Y())) { // around
680  if (rMeeting.X()<aP2.X()) {
681  aP3.setX(rRect.Left() );
682  if (rMeeting.X()<aP3.X()) aP3.setX(rMeeting.X() );
683  } else {
684  aP3.setX(rRect.Right() );
685  if (rMeeting.X()>aP3.X()) aP3.setX(rMeeting.X() );
686  }
687  aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
688  if (aP3.X()!=rMeeting.X()) {
689  aP3.setY(rMeeting.Y() );
690  aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
691  }
692  }
693 #ifdef DBG_UTIL
694  if (aXP.GetPointCount()>4) {
695  OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
696  }
697 #endif
698  return aXP;
699 }
700 
702 {
703  Point aPt1,aPt2;
704  SdrGluePoint aGP1,aGP2;
706  tools::Rectangle aBoundRect1;
707  tools::Rectangle aBoundRect2;
708  tools::Rectangle aBewareRect1;
709  tools::Rectangle aBewareRect2;
710  // first, get the old corner points
711  if (rTrack0.GetPointCount()!=0) {
712  aPt1=rTrack0[0];
713  sal_uInt16 nSiz=rTrack0.GetPointCount();
714  nSiz--;
715  aPt2=rTrack0[nSiz];
716  } else {
717  if (!aOutRect.IsEmpty()) {
718  aPt1=aOutRect.TopLeft();
719  aPt2=aOutRect.BottomRight();
720  }
721  }
722 
723  // #i54102# To allow interactive preview, do also if not inserted
724  const bool bCon1(nullptr != rCon1.pObj && rCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
725  const bool bCon2(nullptr != rCon2.pObj && rCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
726  const SfxItemSet& rSet = GetObjectItemSet();
727 
728  if (bCon1)
729  {
730  if (rCon1.pObj==static_cast<SdrObject const *>(this))
731  {
732  // check, just in case
733  aBoundRect1=aOutRect;
734  }
735  else
736  {
737  aBoundRect1 = rCon1.pObj->GetCurrentBoundRect();
738  }
739 
740  aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
741  aBewareRect1=aBoundRect1;
742  sal_Int32 nH = rSet.Get(SDRATTR_EDGENODE1HORZDIST).GetValue();
743  sal_Int32 nV = rSet.Get(SDRATTR_EDGENODE1VERTDIST).GetValue();
744  aBewareRect1.AdjustLeft( -nH );
745  aBewareRect1.AdjustRight(nH );
746  aBewareRect1.AdjustTop( -nV );
747  aBewareRect1.AdjustBottom(nV );
748  }
749  else
750  {
751  aBoundRect1=tools::Rectangle(aPt1,aPt1);
752  aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
753  aBewareRect1=aBoundRect1;
754  }
755 
756  if (bCon2)
757  {
758  if (rCon2.pObj==static_cast<SdrObject const *>(this))
759  { // check, just in case
760  aBoundRect2=aOutRect;
761  }
762  else
763  {
764  aBoundRect2 = rCon2.pObj->GetCurrentBoundRect();
765  }
766 
767  aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
768  aBewareRect2=aBoundRect2;
769  sal_Int32 nH = rSet.Get(SDRATTR_EDGENODE2HORZDIST).GetValue();
770  sal_Int32 nV = rSet.Get(SDRATTR_EDGENODE2VERTDIST).GetValue();
771  aBewareRect2.AdjustLeft( -nH );
772  aBewareRect2.AdjustRight(nH );
773  aBewareRect2.AdjustTop( -nV );
774  aBewareRect2.AdjustBottom(nV );
775  }
776  else
777  {
778  aBoundRect2=tools::Rectangle(aPt2,aPt2);
779  aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
780  aBewareRect2=aBoundRect2;
781  }
782 
783  XPolygon aBestXP;
784  sal_uIntPtr nBestQual=0xFFFFFFFF;
785  SdrEdgeInfoRec aBestInfo;
786  bool bAuto1=bCon1 && rCon1.bBestVertex;
787  bool bAuto2=bCon2 && rCon2.bBestVertex;
788  if (bAuto1) rCon1.bAutoVertex=true;
789  if (bAuto2) rCon2.bAutoVertex=true;
790  sal_uInt16 nBestAuto1=0;
791  sal_uInt16 nBestAuto2=0;
792  sal_uInt16 nCount1=bAuto1 ? 4 : 1;
793  sal_uInt16 nCount2=bAuto2 ? 4 : 1;
794 
795  for (sal_uInt16 nNum1=0; nNum1<nCount1; nNum1++)
796  {
797  if (bAuto1) rCon1.nConId=nNum1;
798  if (bCon1 && rCon1.TakeGluePoint(aGP1))
799  {
800  aPt1=aGP1.GetPos();
801  nEsc1=aGP1.GetEscDir();
802  if (nEsc1==SdrEscapeDirection::SMART) nEsc1=ImpCalcEscAngle(rCon1.pObj,aPt1-rCon1.aObjOfs);
803  }
804  for (sal_uInt16 nNum2=0; nNum2<nCount2; nNum2++)
805  {
806  if (bAuto2) rCon2.nConId=nNum2;
807  if (bCon2 && rCon2.TakeGluePoint(aGP2))
808  {
809  aPt2=aGP2.GetPos();
810  nEsc2=aGP2.GetEscDir();
811  if (nEsc2==SdrEscapeDirection::SMART) nEsc2=ImpCalcEscAngle(rCon2.pObj,aPt2-rCon2.aObjOfs);
812  }
813  for (long nA1=0; nA1<36000; nA1+=9000)
814  {
816  for (long nA2=0; nA2<36000; nA2+=9000)
817  {
819  if ((nEsc1&nE1) && (nEsc2&nE2))
820  {
821  sal_uIntPtr nQual=0;
822  SdrEdgeInfoRec aInfo;
823  if (pInfo!=nullptr) aInfo=*pInfo;
824  XPolygon aXP(ImpCalcEdgeTrack(aPt1,nA1,aBoundRect1,aBewareRect1,aPt2,nA2,aBoundRect2,aBewareRect2,&nQual,&aInfo));
825  if (nQual<nBestQual)
826  {
827  aBestXP=aXP;
828  nBestQual=nQual;
829  aBestInfo=aInfo;
830  nBestAuto1=nNum1;
831  nBestAuto2=nNum2;
832  }
833  }
834  }
835  }
836  }
837  }
838  if (bAuto1) rCon1.nConId=nBestAuto1;
839  if (bAuto2) rCon2.nConId=nBestAuto2;
840  if (pInfo!=nullptr) *pInfo=aBestInfo;
841  return aBestXP;
842 }
843 
844 XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const Point& rPt1, long nAngle1, const tools::Rectangle& rBoundRect1, const tools::Rectangle& rBewareRect1,
845  const Point& rPt2, long nAngle2, const tools::Rectangle& rBoundRect2, const tools::Rectangle& rBewareRect2,
846  sal_uIntPtr* pnQuality, SdrEdgeInfoRec* pInfo) const
847 {
848  SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
849  bool bRts1=nAngle1==0;
850  bool bObn1=nAngle1==9000;
851  bool bLks1=nAngle1==18000;
852  bool bUnt1=nAngle1==27000;
853  bool bHor1=bLks1 || bRts1;
854  bool bVer1=bObn1 || bUnt1;
855  bool bRts2=nAngle2==0;
856  bool bObn2=nAngle2==9000;
857  bool bLks2=nAngle2==18000;
858  bool bUnt2=nAngle2==27000;
859  bool bHor2=bLks2 || bRts2;
860  bool bVer2=bObn2 || bUnt2;
861  bool bInfo=pInfo!=nullptr;
862  if (bInfo) {
863  pInfo->nAngle1=nAngle1;
864  pInfo->nAngle2=nAngle2;
865  pInfo->nObj1Lines=1;
866  pInfo->nObj2Lines=1;
867  pInfo->nMiddleLine=0xFFFF;
868  }
869  Point aPt1(rPt1);
870  Point aPt2(rPt2);
871  tools::Rectangle aBoundRect1 (rBoundRect1 );
872  tools::Rectangle aBoundRect2 (rBoundRect2 );
873  tools::Rectangle aBewareRect1(rBewareRect1);
874  tools::Rectangle aBewareRect2(rBewareRect2);
875  Point aMeeting((aPt1.X()+aPt2.X()+1)/2,(aPt1.Y()+aPt2.Y()+1)/2);
876  if (eKind==SdrEdgeKind::OneLine) {
877  XPolygon aXP(2);
878  aXP[0]=rPt1;
879  aXP[1]=rPt2;
880  if (pnQuality!=nullptr) {
881  *pnQuality=std::abs(rPt1.X()-rPt2.X())+std::abs(rPt1.Y()-rPt2.Y());
882  }
883  return aXP;
884  } else if (eKind==SdrEdgeKind::ThreeLines) {
885  XPolygon aXP(4);
886  aXP[0]=rPt1;
887  aXP[1]=rPt1;
888  aXP[2]=rPt2;
889  aXP[3]=rPt2;
890  if (bRts1) aXP[1].setX(aBewareRect1.Right() ); //+=500;
891  if (bObn1) aXP[1].setY(aBewareRect1.Top() ); //-=500;
892  if (bLks1) aXP[1].setX(aBewareRect1.Left() ); //-=500;
893  if (bUnt1) aXP[1].setY(aBewareRect1.Bottom() ); //+=500;
894  if (bRts2) aXP[2].setX(aBewareRect2.Right() ); //+=500;
895  if (bObn2) aXP[2].setY(aBewareRect2.Top() ); //-=500;
896  if (bLks2) aXP[2].setX(aBewareRect2.Left() ); //-=500;
897  if (bUnt2) aXP[2].setY(aBewareRect2.Bottom() ); //+=500;
898  if (pnQuality!=nullptr) {
899  long nQ=std::abs(aXP[1].X()-aXP[0].X())+std::abs(aXP[1].Y()-aXP[0].Y());
900  nQ+=std::abs(aXP[2].X()-aXP[1].X())+std::abs(aXP[2].Y()-aXP[1].Y());
901  nQ+=std::abs(aXP[3].X()-aXP[2].X())+std::abs(aXP[3].Y()-aXP[2].Y());
902  *pnQuality=nQ;
903  }
904  if (bInfo) {
905  pInfo->nObj1Lines=2;
906  pInfo->nObj2Lines=2;
907  if (bHor1) {
908  aXP[1].AdjustX(pInfo->aObj1Line2.X() );
909  } else {
910  aXP[1].AdjustY(pInfo->aObj1Line2.Y() );
911  }
912  if (bHor2) {
913  aXP[2].AdjustX(pInfo->aObj2Line2.X() );
914  } else {
915  aXP[2].AdjustY(pInfo->aObj2Line2.Y() );
916  }
917  }
918  return aXP;
919  }
920  sal_uInt16 nIntersections=0;
921  {
922  Point aC1(aBewareRect1.Center());
923  Point aC2(aBewareRect2.Center());
924  if (aBewareRect1.Left()<=aBewareRect2.Right() && aBewareRect1.Right()>=aBewareRect2.Left()) {
925  // overlapping on the x axis
926  long n1=std::max(aBewareRect1.Left(),aBewareRect2.Left());
927  long n2=std::min(aBewareRect1.Right(),aBewareRect2.Right());
928  aMeeting.setX((n1+n2+1)/2 );
929  } else {
930  // otherwise the center point of the empty space
931  if (aC1.X()<aC2.X()) {
932  aMeeting.setX((aBewareRect1.Right()+aBewareRect2.Left()+1)/2 );
933  } else {
934  aMeeting.setX((aBewareRect1.Left()+aBewareRect2.Right()+1)/2 );
935  }
936  }
937  if (aBewareRect1.Top()<=aBewareRect2.Bottom() && aBewareRect1.Bottom()>=aBewareRect2.Top()) {
938  // overlapping on the x axis
939  long n1=std::max(aBewareRect1.Top(),aBewareRect2.Top());
940  long n2=std::min(aBewareRect1.Bottom(),aBewareRect2.Bottom());
941  aMeeting.setY((n1+n2+1)/2 );
942  } else {
943  // otherwise the center point of the empty space
944  if (aC1.Y()<aC2.Y()) {
945  aMeeting.setY((aBewareRect1.Bottom()+aBewareRect2.Top()+1)/2 );
946  } else {
947  aMeeting.setY((aBewareRect1.Top()+aBewareRect2.Bottom()+1)/2 );
948  }
949  }
950  // Here, there are three cases:
951  // 1. both go into the same direction
952  // 2. both go into opposite directions
953  // 3. one is vertical, the other is horizontal
954  long nXMin=std::min(aBewareRect1.Left(),aBewareRect2.Left());
955  long nXMax=std::max(aBewareRect1.Right(),aBewareRect2.Right());
956  long nYMin=std::min(aBewareRect1.Top(),aBewareRect2.Top());
957  long nYMax=std::max(aBewareRect1.Bottom(),aBewareRect2.Bottom());
958  bool bBewareOverlap=aBewareRect1.Right()>aBewareRect2.Left() && aBewareRect1.Left()<aBewareRect2.Right() &&
959  aBewareRect1.Bottom()>aBewareRect2.Top() && aBewareRect1.Top()<aBewareRect2.Bottom();
960  unsigned nMainCase=3;
961  if (nAngle1==nAngle2) nMainCase=1;
962  else if ((bHor1 && bHor2) || (bVer1 && bVer2)) nMainCase=2;
963  if (nMainCase==1) { // case 1 (both go in one direction) is possible
964  if (bVer1) aMeeting.setX((aPt1.X()+aPt2.X()+1)/2 ); // Here, this is better than
965  if (bHor1) aMeeting.setY((aPt1.Y()+aPt2.Y()+1)/2 ); // using center point of empty space
966  // bX1Ok means that the vertical exiting Obj1 doesn't conflict with Obj2, ...
967  bool bX1Ok=aPt1.X()<=aBewareRect2.Left() || aPt1.X()>=aBewareRect2.Right();
968  bool bX2Ok=aPt2.X()<=aBewareRect1.Left() || aPt2.X()>=aBewareRect1.Right();
969  bool bY1Ok=aPt1.Y()<=aBewareRect2.Top() || aPt1.Y()>=aBewareRect2.Bottom();
970  bool bY2Ok=aPt2.Y()<=aBewareRect1.Top() || aPt2.Y()>=aBewareRect1.Bottom();
971  if (bLks1 && (bY1Ok || aBewareRect1.Left()<aBewareRect2.Right()) && (bY2Ok || aBewareRect2.Left()<aBewareRect1.Right())) {
972  aMeeting.setX(nXMin );
973  }
974  if (bRts1 && (bY1Ok || aBewareRect1.Right()>aBewareRect2.Left()) && (bY2Ok || aBewareRect2.Right()>aBewareRect1.Left())) {
975  aMeeting.setX(nXMax );
976  }
977  if (bObn1 && (bX1Ok || aBewareRect1.Top()<aBewareRect2.Bottom()) && (bX2Ok || aBewareRect2.Top()<aBewareRect1.Bottom())) {
978  aMeeting.setY(nYMin );
979  }
980  if (bUnt1 && (bX1Ok || aBewareRect1.Bottom()>aBewareRect2.Top()) && (bX2Ok || aBewareRect2.Bottom()>aBewareRect1.Top())) {
981  aMeeting.setY(nYMax );
982  }
983  } else if (nMainCase==2) {
984  // case 2:
985  if (bHor1) { // both horizontal
986  /* 9 sub-cases:
987  (legend: line exits to the left (-|), right (|-))
988 
989  2.1: Facing; overlap only on y axis
990  * * *
991  |--| *
992  * * *
993 
994  2.2, 2.3: Facing, offset vertically; no overlap on either
995  axis
996  |- * * * * *
997  * -| * * -| *
998  * * * , * * *
999 
1000  2.4, 2.5: One below the other; overlap only on y axis
1001  * |- * * * *
1002  * -| * * -| *
1003  * * * , * |- *
1004 
1005  2.6, 2.7: Not facing, offset vertically; no overlap on either
1006  axis
1007  * * |- * * *
1008  * -| * * -| *
1009  * * * , * * |-
1010 
1011  2.8: Not facing; overlap only on y axis
1012  * * *
1013  * -| |-
1014  * * *
1015 
1016  2.9: The objects's BewareRects overlap on x and y axis
1017 
1018  These cases, with some modifications are also valid for
1019  horizontal line exits.
1020  Cases 2.1 through 2.7 are covered well enough with the
1021  default meetings. Only for cases 2.8 and 2.9 do we determine
1022  special meeting points here.
1023  */
1024 
1025  // normalization; be aR1 the one exiting to the right,
1026  // be aR2 the one exiting to the left
1027  tools::Rectangle aBewR1(bRts1 ? aBewareRect1 : aBewareRect2);
1028  tools::Rectangle aBewR2(bRts1 ? aBewareRect2 : aBewareRect1);
1029  tools::Rectangle aBndR1(bRts1 ? aBoundRect1 : aBoundRect2);
1030  tools::Rectangle aBndR2(bRts1 ? aBoundRect2 : aBoundRect1);
1031  if (aBewR1.Bottom()>aBewR2.Top() && aBewR1.Top()<aBewR2.Bottom()) {
1032  // overlap on y axis; cases 2.1, 2.8, 2.9
1033  if (aBewR1.Right()>aBewR2.Left()) {
1034  /* Cases 2.8, 2.9:
1035  Case 2.8: always going around on the outside
1036  (bDirect=false).
1037 
1038  Case 2.9 could also be a direct connection (in the
1039  case that the BewareRects overlap only slightly and
1040  the BoundRects don't overlap at all and if the
1041  line exits would otherwise violate the respective
1042  other object's BewareRect).
1043  */
1044  bool bCase29Direct = false;
1045  bool bCase29=aBewR1.Right()>aBewR2.Left();
1046  if (aBndR1.Right()<=aBndR2.Left()) { // case 2.9 without BoundRect overlap
1047  if ((aPt1.Y()>aBewareRect2.Top() && aPt1.Y()<aBewareRect2.Bottom()) ||
1048  (aPt2.Y()>aBewareRect1.Top() && aPt2.Y()<aBewareRect1.Bottom())) {
1049  bCase29Direct = true;
1050  }
1051  }
1052  if (!bCase29Direct) {
1053  bool bObenLang=std::abs(nYMin-aMeeting.Y())<=std::abs(nYMax-aMeeting.Y());
1054  if (bObenLang) {
1055  aMeeting.setY(nYMin );
1056  } else {
1057  aMeeting.setY(nYMax );
1058  }
1059  if (bCase29) {
1060  // now make sure that the surrounded object
1061  // isn't traversed
1062  if ((aBewR1.Center().Y()<aBewR2.Center().Y()) != bObenLang) {
1063  aMeeting.setX(aBewR2.Right() );
1064  } else {
1065  aMeeting.setX(aBewR1.Left() );
1066  }
1067  }
1068  } else {
1069  // We need a direct connection (3-line Z connection),
1070  // because we have to violate the BewareRects.
1071  // Use rule of three to scale down the BewareRects.
1072  long nWant1=aBewR1.Right()-aBndR1.Right(); // distance at Obj1
1073  long nWant2=aBndR2.Left()-aBewR2.Left(); // distance at Obj2
1074  long nSpace=aBndR2.Left()-aBndR1.Right(); // available space
1075  long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
1076  long nGet2=nSpace-nGet1;
1077  if (bRts1) { // revert normalization
1078  aBewareRect1.AdjustRight(nGet1-nWant1 );
1079  aBewareRect2.AdjustLeft( -(nGet2-nWant2) );
1080  } else {
1081  aBewareRect2.AdjustRight(nGet1-nWant1 );
1082  aBewareRect1.AdjustLeft( -(nGet2-nWant2) );
1083  }
1084  nIntersections++; // lower quality
1085  }
1086  }
1087  }
1088  } else if (bVer1) { // both horizontal
1089  tools::Rectangle aBewR1(bUnt1 ? aBewareRect1 : aBewareRect2);
1090  tools::Rectangle aBewR2(bUnt1 ? aBewareRect2 : aBewareRect1);
1091  tools::Rectangle aBndR1(bUnt1 ? aBoundRect1 : aBoundRect2);
1092  tools::Rectangle aBndR2(bUnt1 ? aBoundRect2 : aBoundRect1);
1093  if (aBewR1.Right()>aBewR2.Left() && aBewR1.Left()<aBewR2.Right()) {
1094  // overlap on y axis; cases 2.1, 2.8, 2.9
1095  if (aBewR1.Bottom()>aBewR2.Top()) {
1096  /* Cases 2.8, 2.9
1097  Case 2.8 always going around on the outside (bDirect=false).
1098 
1099  Case 2.9 could also be a direct connection (in the
1100  case that the BewareRects overlap only slightly and
1101  the BoundRects don't overlap at all and if the
1102  line exits would otherwise violate the respective
1103  other object's BewareRect).
1104  */
1105  bool bCase29Direct = false;
1106  bool bCase29=aBewR1.Bottom()>aBewR2.Top();
1107  if (aBndR1.Bottom()<=aBndR2.Top()) { // case 2.9 without BoundRect overlap
1108  if ((aPt1.X()>aBewareRect2.Left() && aPt1.X()<aBewareRect2.Right()) ||
1109  (aPt2.X()>aBewareRect1.Left() && aPt2.X()<aBewareRect1.Right())) {
1110  bCase29Direct = true;
1111  }
1112  }
1113  if (!bCase29Direct) {
1114  bool bLinksLang=std::abs(nXMin-aMeeting.X())<=std::abs(nXMax-aMeeting.X());
1115  if (bLinksLang) {
1116  aMeeting.setX(nXMin );
1117  } else {
1118  aMeeting.setX(nXMax );
1119  }
1120  if (bCase29) {
1121  // now make sure that the surrounded object
1122  // isn't traversed
1123  if ((aBewR1.Center().X()<aBewR2.Center().X()) != bLinksLang) {
1124  aMeeting.setY(aBewR2.Bottom() );
1125  } else {
1126  aMeeting.setY(aBewR1.Top() );
1127  }
1128  }
1129  } else {
1130  // We need a direct connection (3-line Z connection),
1131  // because we have to violate the BewareRects.
1132  // Use rule of three to scale down the BewareRects.
1133  long nWant1=aBewR1.Bottom()-aBndR1.Bottom(); // difference at Obj1
1134  long nWant2=aBndR2.Top()-aBewR2.Top(); // difference at Obj2
1135  long nSpace=aBndR2.Top()-aBndR1.Bottom(); // available space
1136  long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
1137  long nGet2=nSpace-nGet1;
1138  if (bUnt1) { // revert normalization
1139  aBewareRect1.AdjustBottom(nGet1-nWant1 );
1140  aBewareRect2.AdjustTop( -(nGet2-nWant2) );
1141  } else {
1142  aBewareRect2.AdjustBottom(nGet1-nWant1 );
1143  aBewareRect1.AdjustTop( -(nGet2-nWant2) );
1144  }
1145  nIntersections++; // lower quality
1146  }
1147  }
1148  }
1149  }
1150  } else if (nMainCase==3) { // case 3: one horizontal, the other vertical
1151  /* legend:
1152  The line exits to the:
1153  -| left
1154 
1155  |- right
1156 
1157  _|_ top
1158 
1159  T bottom
1160 
1161  * . * . * -- no overlap, at most might touch
1162  . . . . . -- overlap
1163  * . |- . * -- same height
1164  . . . . . -- overlap
1165  * . * . * -- no overlap, at most might touch
1166 
1167  Overall, there are 96 possible constellations, some of these can't even
1168  be unambiguously assigned to a certain case/method of handling.
1169 
1170 
1171  3.1: All those constellations that are covered reasonably well
1172  by the default MeetingPoint (20+12).
1173 
1174  T T T . _|_ _|_ . T T T these 12 * . * T * * . * . * * T * . * * . * . *
1175  . . . . _|_ _|_ . . . . constellations . . . . . . . . . T . . . . . T . . . .
1176  * . |- . * * . -| . * are covered * . |- . _|_ * . |- . T _|_ . -| . * T . -| . *
1177  . . . . T T . . . . only in . . . . _|_ . . . . . _|_ . . . . . . . . .
1178  _|__|__|_ . T T . _|__|__|_ part: * . * _|_ * * . * . * * _|_ * . * * . * . *
1179 
1180  The last 16 of these cases can be excluded, if the objects face each other openly.
1181 
1182 
1183  3.2: The objects face each other openly, thus a connection using only two lines is possible (4+20);
1184  This case is priority #1.
1185  * . * . T T . * . * these 20 * . * T * * T * . * * . * . * * . * . *
1186  . . . . . . . . . . constellations . . . T T T T . . . . . . . . . . . . .
1187  * . |- . * * . -| . * are covered * . |-_|__|_ _|__|_-| . * * . |- T T T T -| . *
1188  . . . . . . . . . . only in . . . _|__|_ _|__|_ . . . . . . . . . . . . .
1189  * . * . _|_ _|_ . * . * part: * . * _|_ * * _|_ * . * * . * . * * . * . *
1190 
1191  3.3: The line exits point away from the other object or miss its back (52+4).
1192  _|__|__|__|_ * * _|__|__|__|_ * . . . * * . * . * these 4 * . * . * * . * . *
1193  _|__|__|__|_ . . _|__|__|__|_ T T T . . . . T T T constellations . . . T . . T . . .
1194  _|__|_ |- . * * . -| _|__|_ T T |- . * * . -| T T are covered * . |- . * * . -| . *
1195  _|__|__|_ . . . . _|__|__|_ T T T T . . T T T T only in . . . _|_ . . _|_ . . .
1196  * . * . * * . * . * T T T T * * T T T T part: * . * . * * . * . *
1197  */
1198 
1199  // case 3.2
1200  tools::Rectangle aTmpR1(aBewareRect1);
1201  tools::Rectangle aTmpR2(aBewareRect2);
1202  if (bBewareOverlap) {
1203  // overlapping BewareRects: use BoundRects for checking for case 3.2
1204  aTmpR1=aBoundRect1;
1205  aTmpR2=aBoundRect2;
1206  }
1207  if ((((bRts1 && aTmpR1.Right ()<=aPt2.X()) || (bLks1 && aTmpR1.Left()>=aPt2.X())) &&
1208  ((bUnt2 && aTmpR2.Bottom()<=aPt1.Y()) || (bObn2 && aTmpR2.Top ()>=aPt1.Y()))) ||
1209  (((bRts2 && aTmpR2.Right ()<=aPt1.X()) || (bLks2 && aTmpR2.Left()>=aPt1.X())) &&
1210  ((bUnt1 && aTmpR1.Bottom()<=aPt2.Y()) || (bObn1 && aTmpR1.Top ()>=aPt2.Y())))) {
1211  // case 3.2 applies: connector with only 2 lines
1212  if (bHor1) {
1213  aMeeting.setX(aPt2.X() );
1214  aMeeting.setY(aPt1.Y() );
1215  } else {
1216  aMeeting.setX(aPt1.X() );
1217  aMeeting.setY(aPt2.Y() );
1218  }
1219  // in the case of overlapping BewareRects:
1220  aBewareRect1=aTmpR1;
1221  aBewareRect2=aTmpR2;
1222  } else if ((((bRts1 && aBewareRect1.Right ()>aBewareRect2.Left ()) ||
1223  (bLks1 && aBewareRect1.Left ()<aBewareRect2.Right ())) &&
1224  ((bUnt2 && aBewareRect2.Bottom()>aBewareRect1.Top ()) ||
1225  (bObn2 && aBewareRect2.Top ()<aBewareRect1.Bottom()))) ||
1226  (((bRts2 && aBewareRect2.Right ()>aBewareRect1.Left ()) ||
1227  (bLks2 && aBewareRect2.Left ()<aBewareRect1.Right ())) &&
1228  ((bUnt1 && aBewareRect1.Bottom()>aBewareRect2.Top ()) ||
1229  (bObn1 && aBewareRect1.Top ()<aBewareRect2.Bottom())))) {
1230  // case 3.3
1231  if (bRts1 || bRts2) { aMeeting.setX(nXMax ); }
1232  if (bLks1 || bLks2) { aMeeting.setX(nXMin ); }
1233  if (bUnt1 || bUnt2) { aMeeting.setY(nYMax ); }
1234  if (bObn1 || bObn2) { aMeeting.setY(nYMin ); }
1235  }
1236  }
1237  }
1238 
1239  XPolygon aXP1(ImpCalcObjToCenter(aPt1,nAngle1,aBewareRect1,aMeeting));
1240  XPolygon aXP2(ImpCalcObjToCenter(aPt2,nAngle2,aBewareRect2,aMeeting));
1241  sal_uInt16 nXP1Cnt=aXP1.GetPointCount();
1242  sal_uInt16 nXP2Cnt=aXP2.GetPointCount();
1243  if (bInfo) {
1244  pInfo->nObj1Lines=nXP1Cnt; if (nXP1Cnt>1) pInfo->nObj1Lines--;
1245  pInfo->nObj2Lines=nXP2Cnt; if (nXP2Cnt>1) pInfo->nObj2Lines--;
1246  }
1247  Point aEP1(aXP1[nXP1Cnt-1]);
1248  Point aEP2(aXP2[nXP2Cnt-1]);
1249  bool bInsMeetingPoint=aEP1.X()!=aEP2.X() && aEP1.Y()!=aEP2.Y();
1250  bool bHorzE1=aEP1.Y()==aXP1[nXP1Cnt-2].Y(); // is last line of XP1 horizontal?
1251  bool bHorzE2=aEP2.Y()==aXP2[nXP2Cnt-2].Y(); // is last line of XP2 horizontal?
1252  if (aEP1==aEP2 && ((bHorzE1 && bHorzE2 && aEP1.Y()==aEP2.Y()) || (!bHorzE1 && !bHorzE2 && aEP1.X()==aEP2.X()))) {
1253  // special casing 'I' connectors
1254  nXP1Cnt--; aXP1.Remove(nXP1Cnt,1);
1255  nXP2Cnt--; aXP2.Remove(nXP2Cnt,1);
1256  }
1257  if (bInsMeetingPoint) {
1258  aXP1.Insert(XPOLY_APPEND,aMeeting,PolyFlags::Normal);
1259  if (bInfo) {
1260  // Inserting a MeetingPoint adds 2 new lines,
1261  // either might become the center line.
1262  if (pInfo->nObj1Lines==pInfo->nObj2Lines) {
1263  pInfo->nObj1Lines++;
1264  pInfo->nObj2Lines++;
1265  } else {
1266  if (pInfo->nObj1Lines>pInfo->nObj2Lines) {
1267  pInfo->nObj2Lines++;
1268  pInfo->nMiddleLine=nXP1Cnt-1;
1269  } else {
1270  pInfo->nObj1Lines++;
1271  pInfo->nMiddleLine=nXP1Cnt;
1272  }
1273  }
1274  }
1275  } else if (bInfo && aEP1!=aEP2 && nXP1Cnt+nXP2Cnt>=4) {
1276  // By connecting both ends, another line is added, this becomes the center line.
1277  pInfo->nMiddleLine=nXP1Cnt-1;
1278  }
1279  sal_uInt16 nNum=aXP2.GetPointCount();
1280  if (aXP1[nXP1Cnt-1]==aXP2[nXP2Cnt-1] && nXP1Cnt>1 && nXP2Cnt>1) nNum--;
1281  while (nNum>0) {
1282  nNum--;
1283  aXP1.Insert(XPOLY_APPEND,aXP2[nNum],PolyFlags::Normal);
1284  }
1285  sal_uInt16 nPointCount=aXP1.GetPointCount();
1286  char cForm;
1287  if (bInfo || pnQuality!=nullptr) {
1288  if (nPointCount==2) cForm='I';
1289  else if (nPointCount==3) cForm='L';
1290  else if (nPointCount==4) { // Z or U
1291  if (nAngle1==nAngle2) cForm='U';
1292  else cForm='Z';
1293  } else if (nPointCount==6) { // S or C or ...
1294  if (nAngle1!=nAngle2) {
1295  // For type S, line 2 has the same direction as line 4.
1296  // For type C, the opposite is true.
1297  Point aP1(aXP1[1]);
1298  Point aP2(aXP1[2]);
1299  Point aP3(aXP1[3]);
1300  Point aP4(aXP1[4]);
1301  if (aP1.Y()==aP2.Y()) { // else both lines are horizontal
1302  if ((aP1.X()<aP2.X())==(aP3.X()<aP4.X())) cForm='S';
1303  else cForm='C';
1304  } else { // else both lines are vertical
1305  if ((aP1.Y()<aP2.Y())==(aP3.Y()<aP4.Y())) cForm='S';
1306  else cForm='C';
1307  }
1308  } else cForm='4'; // else is case 3 with 5 lines
1309  } else cForm='?';
1310  // more shapes:
1311  if (bInfo) {
1312  if (cForm=='I' || cForm=='L' || cForm=='Z' || cForm=='U') {
1313  pInfo->nObj1Lines=1;
1314  pInfo->nObj2Lines=1;
1315  if (cForm=='Z' || cForm=='U') {
1316  pInfo->nMiddleLine=1;
1317  } else {
1318  pInfo->nMiddleLine=0xFFFF;
1319  }
1320  } else if (cForm=='S' || cForm=='C') {
1321  pInfo->nObj1Lines=2;
1322  pInfo->nObj2Lines=2;
1323  pInfo->nMiddleLine=2;
1324  }
1325  }
1326  }
1327  else
1328  {
1329  cForm = 0;
1330  }
1331  if (pnQuality!=nullptr) {
1332  sal_uIntPtr nQual=0;
1333  sal_uIntPtr nQual0=nQual; // prevent overruns
1334  bool bOverflow = false;
1335  Point aPt0(aXP1[0]);
1336  for (sal_uInt16 nPntNum=1; nPntNum<nPointCount; nPntNum++) {
1337  Point aPt1b(aXP1[nPntNum]);
1338  nQual+=std::abs(aPt1b.X()-aPt0.X())+std::abs(aPt1b.Y()-aPt0.Y());
1339  if (nQual<nQual0) bOverflow = true;
1340  nQual0=nQual;
1341  aPt0=aPt1b;
1342  }
1343 
1344  sal_uInt16 nTmp=nPointCount;
1345  if (cForm=='Z') {
1346  nTmp=2; // Z shape with good quality (nTmp=2 instead of 4)
1347  sal_uIntPtr n1=std::abs(aXP1[1].X()-aXP1[0].X())+std::abs(aXP1[1].Y()-aXP1[0].Y());
1348  sal_uIntPtr n2=std::abs(aXP1[2].X()-aXP1[1].X())+std::abs(aXP1[2].Y()-aXP1[1].Y());
1349  sal_uIntPtr n3=std::abs(aXP1[3].X()-aXP1[2].X())+std::abs(aXP1[3].Y()-aXP1[2].Y());
1350  // try to make lines lengths similar
1351  sal_uIntPtr nBesser=0;
1352  n1+=n3;
1353  n3=n2/4;
1354  if (n1>=n2) nBesser=6;
1355  else if (n1>=3*n3) nBesser=4;
1356  else if (n1>=2*n3) nBesser=2;
1357  if (aXP1[0].Y()!=aXP1[1].Y()) nBesser++; // vertical starting line gets a plus (for H/V-Prio)
1358  if (nQual>nBesser) nQual-=nBesser; else nQual=0;
1359  }
1360  if (nTmp>=3) {
1361  nQual0=nQual;
1362  nQual+=static_cast<sal_uIntPtr>(nTmp)*0x01000000;
1363  if (nQual<nQual0 || nTmp>15) bOverflow = true;
1364  }
1365  if (nPointCount>=2) { // check exit angle again
1366  Point aP1(aXP1[1]); aP1-=aXP1[0];
1367  Point aP2(aXP1[nPointCount-2]); aP2-=aXP1[nPointCount-1];
1368  long nAng1=0; if (aP1.X()<0) nAng1=18000; if (aP1.Y()>0) nAng1=27000;
1369  if (aP1.Y()<0) nAng1=9000;
1370  if (aP1.X()!=0 && aP1.Y()!=0) nAng1=1; // slant?!
1371  long nAng2=0; if (aP2.X()<0) nAng2=18000; if (aP2.Y()>0) nAng2=27000;
1372  if (aP2.Y()<0) nAng2=9000;
1373  if (aP2.X()!=0 && aP2.Y()!=0) nAng2=1; // slant?!
1374  if (nAng1!=nAngle1) nIntersections++;
1375  if (nAng2!=nAngle2) nIntersections++;
1376  }
1377 
1378  // For the quality check, use the original Rects and at the same time
1379  // check whether one them was scaled down for the calculation of the
1380  // Edges (e. g. case 2.9)
1381  aBewareRect1=rBewareRect1;
1382  aBewareRect2=rBewareRect2;
1383 
1384  for (sal_uInt16 i=0; i<nPointCount; i++) {
1385  Point aPt1b(aXP1[i]);
1386  bool b1=aPt1b.X()>aBewareRect1.Left() && aPt1b.X()<aBewareRect1.Right() &&
1387  aPt1b.Y()>aBewareRect1.Top() && aPt1b.Y()<aBewareRect1.Bottom();
1388  bool b2=aPt1b.X()>aBewareRect2.Left() && aPt1b.X()<aBewareRect2.Right() &&
1389  aPt1b.Y()>aBewareRect2.Top() && aPt1b.Y()<aBewareRect2.Bottom();
1390  sal_uInt16 nInt0=nIntersections;
1391  if (i==0 || i==nPointCount-1) {
1392  if (b1 && b2) nIntersections++;
1393  } else {
1394  if (b1) nIntersections++;
1395  if (b2) nIntersections++;
1396  }
1397  // check for overlaps
1398  if (i>0 && nInt0==nIntersections) {
1399  if (aPt0.Y()==aPt1b.Y()) { // horizontal line
1400  if (aPt0.Y()>aBewareRect1.Top() && aPt0.Y()<aBewareRect1.Bottom() &&
1401  ((aPt0.X()<=aBewareRect1.Left() && aPt1b.X()>=aBewareRect1.Right()) ||
1402  (aPt1b.X()<=aBewareRect1.Left() && aPt0.X()>=aBewareRect1.Right()))) nIntersections++;
1403  if (aPt0.Y()>aBewareRect2.Top() && aPt0.Y()<aBewareRect2.Bottom() &&
1404  ((aPt0.X()<=aBewareRect2.Left() && aPt1b.X()>=aBewareRect2.Right()) ||
1405  (aPt1b.X()<=aBewareRect2.Left() && aPt0.X()>=aBewareRect2.Right()))) nIntersections++;
1406  } else { // vertical line
1407  if (aPt0.X()>aBewareRect1.Left() && aPt0.X()<aBewareRect1.Right() &&
1408  ((aPt0.Y()<=aBewareRect1.Top() && aPt1b.Y()>=aBewareRect1.Bottom()) ||
1409  (aPt1b.Y()<=aBewareRect1.Top() && aPt0.Y()>=aBewareRect1.Bottom()))) nIntersections++;
1410  if (aPt0.X()>aBewareRect2.Left() && aPt0.X()<aBewareRect2.Right() &&
1411  ((aPt0.Y()<=aBewareRect2.Top() && aPt1b.Y()>=aBewareRect2.Bottom()) ||
1412  (aPt1b.Y()<=aBewareRect2.Top() && aPt0.Y()>=aBewareRect2.Bottom()))) nIntersections++;
1413  }
1414  }
1415  aPt0=aPt1b;
1416  }
1417  if (nPointCount<=1) nIntersections++;
1418  nQual0=nQual;
1419  nQual+=static_cast<sal_uIntPtr>(nIntersections)*0x10000000;
1420  if (nQual<nQual0 || nIntersections>15) bOverflow = true;
1421 
1422  if (bOverflow || nQual==0xFFFFFFFF) nQual=0xFFFFFFFE;
1423  *pnQuality=nQual;
1424  }
1425  if (bInfo) { // now apply line offsets to aXP1
1426  if (pInfo->nMiddleLine!=0xFFFF) {
1427  sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::MiddleLine,aXP1);
1428  if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::MiddleLine,aXP1)) {
1429  aXP1[nIdx].AdjustY(pInfo->aMiddleLine.Y() );
1430  aXP1[nIdx+1].AdjustY(pInfo->aMiddleLine.Y() );
1431  } else {
1432  aXP1[nIdx].AdjustX(pInfo->aMiddleLine.X() );
1433  aXP1[nIdx+1].AdjustX(pInfo->aMiddleLine.X() );
1434  }
1435  }
1436  if (pInfo->nObj1Lines>=2) {
1437  sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line2,aXP1);
1438  if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line2,aXP1)) {
1439  aXP1[nIdx].AdjustY(pInfo->aObj1Line2.Y() );
1440  aXP1[nIdx+1].AdjustY(pInfo->aObj1Line2.Y() );
1441  } else {
1442  aXP1[nIdx].AdjustX(pInfo->aObj1Line2.X() );
1443  aXP1[nIdx+1].AdjustX(pInfo->aObj1Line2.X() );
1444  }
1445  }
1446  if (pInfo->nObj1Lines>=3) {
1447  sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line3,aXP1);
1448  if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line3,aXP1)) {
1449  aXP1[nIdx].AdjustY(pInfo->aObj1Line3.Y() );
1450  aXP1[nIdx+1].AdjustY(pInfo->aObj1Line3.Y() );
1451  } else {
1452  aXP1[nIdx].AdjustX(pInfo->aObj1Line3.X() );
1453  aXP1[nIdx+1].AdjustX(pInfo->aObj1Line3.X() );
1454  }
1455  }
1456  if (pInfo->nObj2Lines>=2) {
1457  sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line2,aXP1);
1458  if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line2,aXP1)) {
1459  aXP1[nIdx].AdjustY(pInfo->aObj2Line2.Y() );
1460  aXP1[nIdx+1].AdjustY(pInfo->aObj2Line2.Y() );
1461  } else {
1462  aXP1[nIdx].AdjustX(pInfo->aObj2Line2.X() );
1463  aXP1[nIdx+1].AdjustX(pInfo->aObj2Line2.X() );
1464  }
1465  }
1466  if (pInfo->nObj2Lines>=3) {
1467  sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line3,aXP1);
1468  if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line3,aXP1)) {
1469  aXP1[nIdx].AdjustY(pInfo->aObj2Line3.Y() );
1470  aXP1[nIdx+1].AdjustY(pInfo->aObj2Line3.Y() );
1471  } else {
1472  aXP1[nIdx].AdjustX(pInfo->aObj2Line3.X() );
1473  aXP1[nIdx+1].AdjustX(pInfo->aObj2Line3.X() );
1474  }
1475  }
1476  }
1477  // make the connector a bezier curve, if appropriate
1478  if (eKind==SdrEdgeKind::Bezier && nPointCount>2) {
1479  Point* pPt1=&aXP1[0];
1480  Point* pPt2=&aXP1[1];
1481  Point* pPt3=&aXP1[nPointCount-2];
1482  Point* pPt4=&aXP1[nPointCount-1];
1483  long dx1=pPt2->X()-pPt1->X();
1484  long dy1=pPt2->Y()-pPt1->Y();
1485  long dx2=pPt3->X()-pPt4->X();
1486  long dy2=pPt3->Y()-pPt4->Y();
1487  if (cForm=='L') { // nPointCount==3
1488  aXP1.SetFlags(1,PolyFlags::Control);
1489  Point aPt3(*pPt2);
1490  aXP1.Insert(2,aPt3,PolyFlags::Control);
1491  nPointCount=aXP1.GetPointCount();
1492  pPt2=&aXP1[1];
1493  pPt3=&aXP1[nPointCount-2];
1494  pPt2->AdjustX( -(dx1/3) );
1495  pPt2->AdjustY( -(dy1/3) );
1496  pPt3->AdjustX( -(dx2/3) );
1497  pPt3->AdjustY( -(dy2/3) );
1498  } else if (nPointCount>=4 && nPointCount<=6) { // Z or U or ...
1499  // To all others, the end points of the original lines become control
1500  // points for now. Thus, we need to do some more work for nPointCount>4!
1501  aXP1.SetFlags(1,PolyFlags::Control);
1502  aXP1.SetFlags(nPointCount-2,PolyFlags::Control);
1503  // distance x1.5
1504  pPt2->AdjustX(dx1/2 );
1505  pPt2->AdjustY(dy1/2 );
1506  pPt3->AdjustX(dx2/2 );
1507  pPt3->AdjustY(dy2/2 );
1508  if (nPointCount==5) {
1509  // add a control point before and after center
1510  Point aCenter(aXP1[2]);
1511  long dx1b=aCenter.X()-aXP1[1].X();
1512  long dy1b=aCenter.Y()-aXP1[1].Y();
1513  long dx2b=aCenter.X()-aXP1[3].X();
1514  long dy2b=aCenter.Y()-aXP1[3].Y();
1515  aXP1.Insert(2,aCenter,PolyFlags::Control);
1516  aXP1.SetFlags(3,PolyFlags::Symmetric);
1517  aXP1.Insert(4,aCenter,PolyFlags::Control);
1518  aXP1[2].AdjustX( -(dx1b/2) );
1519  aXP1[2].AdjustY( -(dy1b/2) );
1520  aXP1[3].AdjustX( -((dx1b+dx2b)/4) );
1521  aXP1[3].AdjustY( -((dy1b+dy2b)/4) );
1522  aXP1[4].AdjustX( -(dx2b/2) );
1523  aXP1[4].AdjustY( -(dy2b/2) );
1524  }
1525  if (nPointCount==6) {
1526  Point aPt1b(aXP1[2]);
1527  Point aPt2b(aXP1[3]);
1528  aXP1.Insert(2,aPt1b,PolyFlags::Control);
1529  aXP1.Insert(5,aPt2b,PolyFlags::Control);
1530  long dx=aPt1b.X()-aPt2b.X();
1531  long dy=aPt1b.Y()-aPt2b.Y();
1532  aXP1[3].AdjustX( -(dx/2) );
1533  aXP1[3].AdjustY( -(dy/2) );
1534  aXP1.SetFlags(3,PolyFlags::Symmetric);
1535  aXP1.Remove(4,1); // because it's identical with aXP1[3]
1536  }
1537  }
1538  }
1539  return aXP1;
1540 }
1541 
1542 /*
1543 There could be a maximum of 64 different developments with 5 lines, a
1544 maximum of 32 developments with 4 lines, a maximum of 16 developments with
1545 3 lines, a maximum of 8 developments with 2 lines.
1546 This gives us a total of 124 possibilities.
1547 Normalized for the 1st exit angle to the right, there remain 31 possibilities.
1548 Now, normalizing away the vertical mirroring, we get to a total of 16
1549 characteristic developments with 1 through 5 lines:
1550 
1551 1 line (type "I") --
1552 
1553 2 lines (type "L") __|
1554 
1555 3 lines (type "U") __ (type "Z") _
1556  __| _|
1557  _ _
1558 4 lines #1 _| #2 | | #3 |_ #4 | |
1559  _| _| _| _|
1560  Of these, #1 is implausible, #2 is a rotated version of #3. This leaves
1561  #2 (from now on referred to as 4.1) and #4 (from now on referred to as 4.2).
1562  _ _
1563 5 lines #1 _| #2 _| #3 ___ #4 _
1564  _| _| _| _| _| |_
1565  _ _ _
1566  #5 |_ #6 |_ #7 _| | #8 ____
1567  _| _| _| |_ _|
1568  Of these, 5.1, 5.2, 5.4 and 5.5 are implausible, 5.7 is a reversed version
1569  of 5.3. This leaves 5.3 (type "4"), 5.6 (type "S") and 5.8 (type "C").
1570 
1571 We now have discerned the 9 basic types to cover all 400 possible constellations
1572 of object positions and exit angles. 4 of the 9 types have got a center
1573 line (CL). The number of object margins per object varies between 0 and 3:
1574 
1575  CL O1 O2 Note
1576 "I": n 0 0
1577 "L": n 0 0
1578 "U": n 0-1 0-1
1579 "Z": y 0 0
1580 4.2: y 0 1 = U+1, respectively 1+U
1581 4.4: n 0-2 0-2 = Z+1
1582 "4": y 0 2 = Z+2
1583 "S": y 1 1 = 1+Z+1
1584 "C": n 0-3 0-3 = 1+U+1
1585 */
1586 
1588 {
1589  const SfxHintId nId = rHint.GetId();
1590  bool bDataChg=nId==SfxHintId::DataChanged;
1591  bool bDying=nId==SfxHintId::Dying;
1592  bool bObj1=aCon1.pObj!=nullptr && aCon1.pObj->GetBroadcaster()==&rBC;
1593  bool bObj2=aCon2.pObj!=nullptr && aCon2.pObj->GetBroadcaster()==&rBC;
1594  if (bDying && (bObj1 || bObj2)) {
1595  // catch Dying, so AttrObj doesn't start broadcasting
1596  // about an alleged change of template
1597  if (bObj1) aCon1.pObj=nullptr;
1598  if (bObj2) aCon2.pObj=nullptr;
1599  return;
1600  }
1601  if ( bObj1 || bObj2 )
1602  {
1603  bEdgeTrackUserDefined = false;
1604  }
1605  SdrTextObj::Notify(rBC,rHint);
1606  if (nNotifyingCount==0) { // a locking flag
1607  nNotifyingCount++;
1608  const SdrHint* pSdrHint = ( rHint.GetId() == SfxHintId::ThisIsAnSdrHint ? static_cast<const SdrHint*>(&rHint) : nullptr );
1609 
1610  if (bDataChg) { // StyleSheet changed
1611  ImpSetAttrToEdgeInfo(); // when changing templates, copy values from Pool to aEdgeInfo
1612  }
1613  if (bDataChg ||
1616  (pSdrHint && pSdrHint->GetKind()==SdrHintKind::ObjectRemoved))
1617  {
1618  // broadcasting only, if on the same page
1619  tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
1621 
1622  // only redraw here, object hasn't actually changed
1623  ActionChanged();
1624 
1626  }
1627  nNotifyingCount--;
1628  }
1629 }
1630 
1635 {
1636  if( nullptr != aCon1.pObj )
1637  {
1638  SfxHint aHint( SfxHintId::DataChanged );
1639  Notify( *const_cast<SfxBroadcaster*>(aCon1.pObj->GetBroadcaster()), aHint );
1640  }
1641 
1642  if( nullptr != aCon2.pObj )
1643  {
1644  SfxHint aHint( SfxHintId::DataChanged );
1645  Notify( *const_cast<SfxBroadcaster*>(aCon2.pObj->GetBroadcaster()), aHint );
1646  }
1647 }
1648 
1650 {
1651  return CloneHelper< SdrEdgeObj >(rTargetModel);
1652 }
1653 
1655 {
1656  if( this == &rObj )
1657  return *this;
1658  SdrTextObj::operator=(rObj);
1659  *pEdgeTrack =*rObj.pEdgeTrack;
1661  aCon1 =rObj.aCon1;
1662  aCon2 =rObj.aCon2;
1663  aCon1.pObj=nullptr;
1664  aCon2.pObj=nullptr;
1665  aEdgeInfo=rObj.aEdgeInfo;
1666  return *this;
1667 }
1668 
1670 {
1671  OUStringBuffer sName(SvxResId(STR_ObjNameSingulEDGE));
1672 
1673  OUString aName(GetName());
1674  if (!aName.isEmpty())
1675  {
1676  sName.append(' ');
1677  sName.append('\'');
1678  sName.append(aName);
1679  sName.append('\'');
1680  }
1681  return sName.makeStringAndClear();
1682 }
1683 
1685 {
1686  return SvxResId(STR_ObjNamePluralEDGE);
1687 }
1688 
1690 {
1691  basegfx::B2DPolyPolygon aPolyPolygon;
1692 
1693  if (bEdgeTrackDirty)
1694  {
1695  const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
1696  }
1697 
1698  if(pEdgeTrack)
1699  {
1700  aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
1701  }
1702 
1703  return aPolyPolygon;
1704 }
1705 
1707 {
1708  if ( !rPoly.count() )
1709  {
1710  bEdgeTrackDirty = true;
1711  bEdgeTrackUserDefined = false;
1712  }
1713  else
1714  {
1715  *pEdgeTrack = XPolygon( rPoly.getB2DPolygon( 0 ) );
1716  bEdgeTrackDirty = false;
1717  bEdgeTrackUserDefined = true;
1718 
1719  // #i110629# also set aRect and maSnapeRect depending on pEdgeTrack
1720  const tools::Rectangle aPolygonBounds(pEdgeTrack->GetBoundRect());
1721  maRect = aPolygonBounds;
1722  maSnapRect = aPolygonBounds;
1723  }
1724 }
1725 
1727 {
1728  basegfx::B2DPolyPolygon aPolyPolygon;
1729 
1730  if (bEdgeTrackDirty)
1731  const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
1732 
1733  aPolyPolygon.append( pEdgeTrack->getB2DPolygon() );
1734 
1735  return aPolyPolygon;
1736 }
1737 
1738 sal_uInt32 SdrEdgeObj::GetHdlCount() const
1739 {
1740  SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
1741  sal_uInt32 nHdlCnt(0);
1742  sal_uInt32 nPointCount(pEdgeTrack->GetPointCount());
1743 
1744  if(nPointCount)
1745  {
1746  nHdlCnt = 2;
1747  if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
1748  {
1749  sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0 ? aEdgeInfo.nObj1Lines - 1 : 0);
1750  sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0 ? aEdgeInfo.nObj2Lines - 1 : 0);
1751  sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1 : 0);
1752  nHdlCnt += nO1 + nO2 + nM;
1753  }
1754  else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
1755  {
1756  if(GetConnectedNode(true))
1757  nHdlCnt++;
1758 
1759  if(GetConnectedNode(false))
1760  nHdlCnt++;
1761  }
1762  }
1763 
1764  return nHdlCnt;
1765 }
1766 
1768 {
1769  sal_uInt32 nPointCount(pEdgeTrack->GetPointCount());
1770  if (nPointCount==0)
1771  return;
1772 
1773  {
1774  std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*pEdgeTrack)[0],SdrHdlKind::Poly));
1775  if (aCon1.pObj!=nullptr && aCon1.bBestVertex) pHdl->Set1PixMore();
1776  pHdl->SetPointNum(0);
1777  rHdlList.AddHdl(std::move(pHdl));
1778  }
1779  {
1780  std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*pEdgeTrack)[sal_uInt16(nPointCount-1)],SdrHdlKind::Poly));
1781  if (aCon2.pObj!=nullptr && aCon2.bBestVertex) pHdl->Set1PixMore();
1782  pHdl->SetPointNum(1);
1783  rHdlList.AddHdl(std::move(pHdl));
1784  }
1785  {
1786  SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
1787  if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
1788  {
1789  sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0 ? aEdgeInfo.nObj1Lines - 1 : 0);
1790  sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0 ? aEdgeInfo.nObj2Lines - 1 : 0);
1791  sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1 : 0);
1792  for(sal_uInt32 i = 0; i < (nO1 + nO2 + nM); ++i)
1793  {
1794  sal_Int32 nPt(0);
1795  sal_uInt32 nNum = i;
1796  std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(Point(),SdrHdlKind::Poly));
1797  if (nNum<nO1) {
1798  nPt=nNum+1;
1799  if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
1800  if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line3);
1801  } else {
1802  nNum=nNum-nO1;
1803  if (nNum<nO2) {
1804  nPt=nPointCount-3-nNum;
1805  if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
1806  if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line3);
1807  } else {
1808  nNum=nNum-nO2;
1809  if (nNum<nM) {
1810  nPt=aEdgeInfo.nMiddleLine;
1811  pHdl->SetLineCode(SdrEdgeLineCode::MiddleLine);
1812  }
1813  }
1814  }
1815  if (nPt>0) {
1816  Point aPos((*pEdgeTrack)[static_cast<sal_uInt16>(nPt)]);
1817  aPos+=(*pEdgeTrack)[static_cast<sal_uInt16>(nPt)+1];
1818  aPos.setX( aPos.X() / 2 );
1819  aPos.setY( aPos.Y() / 2 );
1820  pHdl->SetPos(aPos);
1821  pHdl->SetPointNum(i + 2);
1822  rHdlList.AddHdl(std::move(pHdl));
1823  }
1824  }
1825  }
1826  else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
1827  {
1828  if(GetConnectedNode(true))
1829  {
1830  Point aPos((*pEdgeTrack)[1]);
1831  std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
1832  pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
1833  pHdl->SetPointNum(2);
1834  rHdlList.AddHdl(std::move(pHdl));
1835  }
1836  if(GetConnectedNode(false))
1837  {
1838  Point aPos((*pEdgeTrack)[2]);
1839  std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
1840  pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
1841  pHdl->SetPointNum(3);
1842  rHdlList.AddHdl(std::move(pHdl));
1843  }
1844  }
1845  }
1846 }
1847 
1849 {
1850  return true;
1851 }
1852 
1854 {
1855  // use Clone operator
1857 
1858  // copy connections for clone, SdrEdgeObj::operator= does not do this
1859  pRetval->ConnectToNode(true, GetConnectedNode(true));
1860  pRetval->ConnectToNode(false, GetConnectedNode(false));
1861 
1862  return SdrObjectUniquePtr(pRetval);
1863 }
1864 
1866 {
1867  if(!rDrag.GetHdl())
1868  return false;
1869 
1870  rDrag.SetEndDragChangesAttributes(true);
1871 
1872  if(rDrag.GetHdl()->GetPointNum() < 2)
1873  {
1874  rDrag.SetNoSnap();
1875  }
1876 
1877  return true;
1878 }
1879 
1881 {
1882  SdrEdgeObj* pOriginalEdge = dynamic_cast< SdrEdgeObj* >(rDragStat.GetHdl()->GetObj());
1883  const bool bOriginalEdgeModified(pOriginalEdge == this);
1884 
1885  if(!bOriginalEdgeModified && pOriginalEdge)
1886  {
1887  // copy connections when clone is modified. This is needed because
1888  // as preparation to this modification the data from the original object
1889  // was copied to the clone using the operator=. As can be seen there,
1890  // that operator does not copy the connections (for good reason)
1891  ConnectToNode(true, pOriginalEdge->GetConnection(true).GetObject());
1892  ConnectToNode(false, pOriginalEdge->GetConnection(false).GetObject());
1893  }
1894 
1895  if(rDragStat.GetHdl()->GetPointNum() < 2)
1896  {
1897  // start or end point connector drag
1898  const bool bDragA(0 == rDragStat.GetHdl()->GetPointNum());
1899  const Point aPointNow(rDragStat.GetNow());
1900 
1901  rDragStat.SetEndDragChangesGeoAndAttributes(true);
1902 
1903  if(rDragStat.GetPageView())
1904  {
1905  SdrObjConnection* pDraggedOne(bDragA ? &aCon1 : &aCon2);
1906 
1907  // clear connection
1908  DisconnectFromNode(bDragA);
1909 
1910  // look for new connection
1911  ImpFindConnector(aPointNow, *rDragStat.GetPageView(), *pDraggedOne, pOriginalEdge);
1912 
1913  if(pDraggedOne->pObj)
1914  {
1915  // if found, officially connect to it; ImpFindConnector only
1916  // sets pObj hard
1917  SdrObject* pNewConnection = pDraggedOne->pObj;
1918  pDraggedOne->pObj = nullptr;
1919  ConnectToNode(bDragA, pNewConnection);
1920  }
1921 
1922  if(rDragStat.GetView() && !bOriginalEdgeModified)
1923  {
1924  // show IA helper, but only do this during IA, so not when the original
1925  // Edge gets modified in the last call
1926  rDragStat.GetView()->SetConnectMarker(*pDraggedOne);
1927  }
1928  }
1929 
1930  if(pEdgeTrack)
1931  {
1932  // change pEdgeTrack to modified position
1933  if(bDragA)
1934  {
1935  (*pEdgeTrack)[0] = aPointNow;
1936  }
1937  else
1938  {
1939  (*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount()-1)] = aPointNow;
1940  }
1941  }
1942 
1943  // reset edge info's offsets, this is an end point drag
1949  }
1950  else
1951  {
1952  // control point connector drag
1953  const ImpEdgeHdl* pEdgeHdl = static_cast<const ImpEdgeHdl*>(rDragStat.GetHdl());
1954  const SdrEdgeLineCode eLineCode = pEdgeHdl->GetLineCode();
1955  const Point aDist(rDragStat.GetNow() - rDragStat.GetStart());
1956  sal_Int32 nDist(pEdgeHdl->IsHorzDrag() ? aDist.X() : aDist.Y());
1957 
1958  nDist += aEdgeInfo.ImpGetLineOffset(eLineCode, *pEdgeTrack);
1959  aEdgeInfo.ImpSetLineOffset(eLineCode, *pEdgeTrack, nDist);
1960  }
1961 
1962  // force recalculation of EdgeTrack
1964  bEdgeTrackDirty=false;
1965 
1966  // save EdgeInfos and mark object as user modified
1968  bEdgeTrackUserDefined = false;
1969 
1970  SetRectsDirty();
1971 
1972  if(bOriginalEdgeModified && rDragStat.GetView())
1973  {
1974  // hide connect marker helper again when original gets changed.
1975  // This happens at the end of the interaction
1976  rDragStat.GetView()->HideConnectMarker();
1977  }
1978 
1979  return true;
1980 }
1981 
1983 {
1984  const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
1985 
1986  if(bCreateComment)
1987  {
1988  return OUString();
1989  }
1990  else
1991  {
1992  return ImpGetDescriptionStr(STR_DragEdgeTail);
1993  }
1994 }
1995 
1996 
1997 basegfx::B2DPolygon SdrEdgeObj::ImplAddConnectorOverlay(SdrDragMethod& rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
1998 {
1999  basegfx::B2DPolygon aResult;
2000 
2001  if(bDetail)
2002  {
2003  SdrObjConnection aMyCon1(aCon1);
2004  SdrObjConnection aMyCon2(aCon2);
2005 
2006  if (bTail1)
2007  {
2008  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon1.aObjOfs.X(), aMyCon1.aObjOfs.Y()));
2009  aMyCon1.aObjOfs.setX( basegfx::fround(aTemp.getX()) );
2010  aMyCon1.aObjOfs.setY( basegfx::fround(aTemp.getY()) );
2011  }
2012 
2013  if (bTail2)
2014  {
2015  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon2.aObjOfs.X(), aMyCon2.aObjOfs.Y()));
2016  aMyCon2.aObjOfs.setX( basegfx::fround(aTemp.getX()) );
2017  aMyCon2.aObjOfs.setY( basegfx::fround(aTemp.getY()) );
2018  }
2019 
2020  SdrEdgeInfoRec aInfo(aEdgeInfo);
2021  XPolygon aXP(ImpCalcEdgeTrack(*pEdgeTrack, aMyCon1, aMyCon2, &aInfo));
2022 
2023  if(aXP.GetPointCount())
2024  {
2025  aResult = aXP.getB2DPolygon();
2026  }
2027  }
2028  else
2029  {
2030  Point aPt1((*pEdgeTrack)[0]);
2031  Point aPt2((*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount() - 1)]);
2032 
2033  if (aCon1.pObj && (aCon1.bBestConn || aCon1.bBestVertex))
2034  aPt1 = aCon1.pObj->GetSnapRect().Center();
2035 
2036  if (aCon2.pObj && (aCon2.bBestConn || aCon2.bBestVertex))
2037  aPt2 = aCon2.pObj->GetSnapRect().Center();
2038 
2039  if (bTail1)
2040  {
2041  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
2042  aPt1.setX( basegfx::fround(aTemp.getX()) );
2043  aPt1.setY( basegfx::fround(aTemp.getY()) );
2044  }
2045 
2046  if (bTail2)
2047  {
2048  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
2049  aPt2.setX( basegfx::fround(aTemp.getX()) );
2050  aPt2.setY( basegfx::fround(aTemp.getY()) );
2051  }
2052 
2053  aResult.append(basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
2054  aResult.append(basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
2055  }
2056 
2057  return aResult;
2058 }
2059 
2061 {
2062  rDragStat.SetNoSnap();
2063  pEdgeTrack->SetPointCount(2);
2064  (*pEdgeTrack)[0]=rDragStat.GetStart();
2065  (*pEdgeTrack)[1]=rDragStat.GetNow();
2066  if (rDragStat.GetPageView()!=nullptr) {
2067  ImpFindConnector(rDragStat.GetStart(),*rDragStat.GetPageView(),aCon1,this);
2068  ConnectToNode(true,aCon1.pObj);
2069  }
2071  return true;
2072 }
2073 
2075 {
2076  sal_uInt16 nMax=pEdgeTrack->GetPointCount();
2077  (*pEdgeTrack)[nMax-1]=rDragStat.GetNow();
2078  if (rDragStat.GetPageView()!=nullptr) {
2079  ImpFindConnector(rDragStat.GetNow(),*rDragStat.GetPageView(),aCon2,this);
2080  rDragStat.GetView()->SetConnectMarker(aCon2);
2081  }
2083  bSnapRectDirty=true;
2084  ConnectToNode(false,aCon2.pObj);
2086  bEdgeTrackDirty=false;
2087  return true;
2088 }
2089 
2091 {
2092  bool bOk=(eCmd==SdrCreateCmd::ForceEnd || rDragStat.GetPointCount()>=2);
2093  if (bOk) {
2094  ConnectToNode(true,aCon1.pObj);
2095  ConnectToNode(false,aCon2.pObj);
2096  if (rDragStat.GetView()!=nullptr) {
2097  rDragStat.GetView()->HideConnectMarker();
2098  }
2099  ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
2100  }
2101  SetRectsDirty();
2102  return bOk;
2103 }
2104 
2106 {
2107  if (rDragStat.GetView()!=nullptr) {
2108  rDragStat.GetView()->HideConnectMarker();
2109  }
2110  return false;
2111 }
2112 
2114 {
2115  if (rDragStat.GetView()!=nullptr) {
2116  rDragStat.GetView()->HideConnectMarker();
2117  }
2118 }
2119 
2121 {
2122  basegfx::B2DPolyPolygon aRetval;
2123  aRetval.append(pEdgeTrack->getB2DPolygon());
2124  return aRetval;
2125 }
2126 
2128 {
2129  return PointerStyle::DrawConnect;
2130 }
2131 
2132 bool SdrEdgeObj::ImpFindConnector(const Point& rPt, const SdrPageView& rPV, SdrObjConnection& rCon, const SdrEdgeObj* pThis, OutputDevice* pOut)
2133 {
2134  rCon.ResetVars();
2135  if (pOut==nullptr) pOut=rPV.GetView().GetFirstOutputDevice();
2136  if (pOut==nullptr) return false;
2137  SdrObjList* pOL=rPV.GetObjList();
2138  const SdrLayerIDSet& rVisLayer=rPV.GetVisibleLayers();
2139  // sensitive area of connectors is twice as large as the one of the handles
2140  sal_uInt16 nMarkHdSiz=rPV.GetView().GetMarkHdlSizePixel();
2141  Size aHalfConSiz(nMarkHdSiz,nMarkHdSiz);
2142  aHalfConSiz=pOut->PixelToLogic(aHalfConSiz);
2143  tools::Rectangle aMouseRect(rPt,rPt);
2144  aMouseRect.AdjustLeft( -(aHalfConSiz.Width()) );
2145  aMouseRect.AdjustTop( -(aHalfConSiz.Height()) );
2146  aMouseRect.AdjustRight(aHalfConSiz.Width() );
2147  aMouseRect.AdjustBottom(aHalfConSiz.Height() );
2148  sal_uInt16 nBoundHitTol=static_cast<sal_uInt16>(aHalfConSiz.Width())/2; if (nBoundHitTol==0) nBoundHitTol=1;
2149  size_t no=pOL->GetObjCount();
2150  bool bFnd = false;
2151  SdrObjConnection aTestCon;
2152 
2153  while (no>0 && !bFnd) {
2154  // issue: group objects on different layers return LayerID=0!
2155  no--;
2156  SdrObject* pObj=pOL->GetObj(no);
2157  if (rVisLayer.IsSet(pObj->GetLayer()) && pObj->IsVisible() && // only visible objects
2158  (pThis==nullptr || pObj!=static_cast<SdrObject const *>(pThis))) // don't connect it to itself
2159  {
2160  tools::Rectangle aObjBound(pObj->GetCurrentBoundRect());
2161  if (aObjBound.IsOver(aMouseRect)) {
2162  aTestCon.ResetVars();
2163  bool bEdge=dynamic_cast<const SdrEdgeObj *>(pObj) != nullptr; // no BestCon for Edge
2164  // User-defined connectors have absolute priority.
2165  // After those come Vertex, Corner and center (Best), all prioritized equally.
2166  // Finally, a HitTest for the object.
2167  const SdrGluePointList* pGPL=pObj->GetGluePointList();
2168  sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
2169  sal_uInt16 nGesAnz=nGluePointCnt+9;
2170  bool bUserFnd = false;
2171  sal_uIntPtr nBestDist=0xFFFFFFFF;
2172  for (sal_uInt16 i=0; i<nGesAnz; i++)
2173  {
2174  bool bUser=i<nGluePointCnt;
2175  bool bVertex=i>=nGluePointCnt+0 && i<nGluePointCnt+4;
2176  bool bCorner=i>=nGluePointCnt+4 && i<nGluePointCnt+8;
2177  bool bCenter=i==nGluePointCnt+8;
2178  bool bOk = false;
2179  Point aConPos;
2180  sal_uInt16 nConNum=i;
2181  if (bUser) {
2182  const SdrGluePoint& rGP=(*pGPL)[nConNum];
2183  aConPos=rGP.GetAbsolutePos(*pObj);
2184  nConNum=rGP.GetId();
2185  bOk = true;
2186  } else if (bVertex && !bUserFnd) {
2187  nConNum=nConNum-nGluePointCnt;
2188  SdrGluePoint aPt(pObj->GetVertexGluePoint(nConNum));
2189  aConPos=aPt.GetAbsolutePos(*pObj);
2190  bOk = true;
2191  } else if (bCorner && !bUserFnd) {
2192  nConNum-=nGluePointCnt+4;
2193  i+=3;
2194  }
2195  else if (bCenter && !bUserFnd && !bEdge)
2196  {
2197  // Suppress default connect at object center
2198  if(!pThis || !pThis->GetSuppressDefaultConnect())
2199  {
2200  // not the edges!
2201  nConNum=0;
2202  aConPos=aObjBound.Center();
2203  bOk = true;
2204  }
2205  }
2206  if (bOk && aMouseRect.IsInside(aConPos)) {
2207  if (bUser) bUserFnd = true;
2208  bFnd = true;
2209  sal_uIntPtr nDist=static_cast<sal_uIntPtr>(std::abs(aConPos.X()-rPt.X()))+static_cast<sal_uIntPtr>(std::abs(aConPos.Y()-rPt.Y()));
2210  if (nDist<nBestDist) {
2211  nBestDist=nDist;
2212  aTestCon.pObj=pObj;
2213  aTestCon.nConId=nConNum;
2214  aTestCon.bAutoCorner=bCorner;
2215  aTestCon.bAutoVertex=bVertex;
2216  aTestCon.bBestConn=false; // bCenter;
2217  aTestCon.bBestVertex=bCenter;
2218  }
2219  }
2220  }
2221  // if no connector is hit, try HitTest again, for BestConnector (=bCenter)
2222  if(!bFnd &&
2223  !bEdge &&
2224  SdrObjectPrimitiveHit(*pObj, rPt, nBoundHitTol, rPV, &rVisLayer, false))
2225  {
2226  // Suppress default connect at object inside bound
2227  if(!pThis || !pThis->GetSuppressDefaultConnect())
2228  {
2229  bFnd = true;
2230  aTestCon.pObj=pObj;
2231  aTestCon.bBestConn=true;
2232  }
2233  }
2234  if (bFnd) {
2235  tools::Rectangle aMouseRect2(rPt,rPt);
2236  aMouseRect.AdjustLeft( -nBoundHitTol );
2237  aMouseRect.AdjustTop( -nBoundHitTol );
2238  aMouseRect.AdjustRight(nBoundHitTol );
2239  aMouseRect.AdjustBottom(nBoundHitTol );
2240  aObjBound.IsOver(aMouseRect2);
2241  }
2242 
2243  }
2244  }
2245  }
2246  rCon=aTestCon;
2247  return bFnd;
2248 }
2249 
2251 {
2252  const tools::Rectangle aOld(GetSnapRect());
2253 
2254  if(aOld != rRect)
2255  {
2256  if (maRect.IsEmpty() && 0 == pEdgeTrack->GetPointCount())
2257  {
2258  // #i110629# When initializing, do not scale on empty Rectangle; this
2259  // will mirror the underlying text object (!)
2260  maRect = rRect;
2261  maSnapRect = rRect;
2262  }
2263  else
2264  {
2265  long nMulX = rRect.Right() - rRect.Left();
2266  long nDivX = aOld.Right() - aOld.Left();
2267  long nMulY = rRect.Bottom() - rRect.Top();
2268  long nDivY = aOld.Bottom() - aOld.Top();
2269  if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2270  if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2271  Fraction aX(nMulX, nDivX);
2272  Fraction aY(nMulY, nDivY);
2273  NbcResize(aOld.TopLeft(), aX, aY);
2274  NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2275  }
2276  }
2277 }
2278 
2279 void SdrEdgeObj::NbcMove(const Size& rSiz)
2280 {
2281  SdrTextObj::NbcMove(rSiz);
2282  MoveXPoly(*pEdgeTrack,rSiz);
2283 }
2284 
2285 void SdrEdgeObj::NbcResize(const Point& rRefPnt, const Fraction& aXFact, const Fraction& aYFact)
2286 {
2287  SdrTextObj::NbcResize(rRefPnt,aXFact,aXFact);
2288  ResizeXPoly(*pEdgeTrack,rRefPnt,aXFact,aYFact);
2289 
2290  // if resize is not from paste, forget user distances
2291  if (!getSdrModelFromSdrObject().IsPasteResize())
2292  {
2298  }
2299 }
2300 
2301 // #i54102# added rotation support
2302 void SdrEdgeObj::NbcRotate(const Point& rRef, long nAngle, double sn, double cs)
2303 {
2305  {
2306  // #i120437# special handling when track is imported, apply
2307  // transformation directly to imported track.
2308  SdrTextObj::NbcRotate(rRef, nAngle, sn, cs);
2309  RotateXPoly(*pEdgeTrack, rRef, sn, cs);
2310  }
2311  else
2312  {
2313  // handle start and end point if not connected
2314  const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2315  const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2316 
2317  if(!bCon1 && pEdgeTrack)
2318  {
2319  RotatePoint((*pEdgeTrack)[0],rRef,sn,cs);
2321  }
2322 
2323  if(!bCon2 && pEdgeTrack)
2324  {
2325  sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
2326  RotatePoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,sn,cs);
2328  }
2329  }
2330 }
2331 
2332 // #i54102# added mirror support
2333 void SdrEdgeObj::NbcMirror(const Point& rRef1, const Point& rRef2)
2334 {
2336  {
2337  // #i120437# special handling when track is imported, apply
2338  // transformation directly to imported track.
2339  SdrTextObj::NbcMirror(rRef1, rRef2);
2340  MirrorXPoly(*pEdgeTrack, rRef1, rRef2);
2341  }
2342  else
2343  {
2344  // handle start and end point if not connected
2345  const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2346  const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2347 
2348  if(!bCon1 && pEdgeTrack)
2349  {
2350  MirrorPoint((*pEdgeTrack)[0],rRef1,rRef2);
2352  }
2353 
2354  if(!bCon2 && pEdgeTrack)
2355  {
2356  sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
2357  MirrorPoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef1,rRef2);
2359  }
2360  }
2361 }
2362 
2363 // #i54102# added shear support
2364 void SdrEdgeObj::NbcShear(const Point& rRef, long nAngle, double tn, bool bVShear)
2365 {
2367  {
2368  // #i120437# special handling when track is imported, apply
2369  // transformation directly to imported track.
2370  SdrTextObj::NbcShear(rRef, nAngle, tn, bVShear);
2371  ShearXPoly(*pEdgeTrack, rRef, tn, bVShear);
2372  }
2373  else
2374  {
2375  // handle start and end point if not connected
2376  const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2377  const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2378 
2379  if(!bCon1 && pEdgeTrack)
2380  {
2381  ShearPoint((*pEdgeTrack)[0],rRef,tn,bVShear);
2383  }
2384 
2385  if(!bCon2 && pEdgeTrack)
2386  {
2387  sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
2388  ShearPoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,tn,bVShear);
2390  }
2391  }
2392 }
2393 
2394 SdrObjectUniquePtr SdrEdgeObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
2395 {
2396  basegfx::B2DPolyPolygon aPolyPolygon;
2397  aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
2398  SdrObjectUniquePtr pRet = ImpConvertMakeObj(aPolyPolygon, false, bBezier);
2399 
2400  if(bAddText)
2401  {
2402  pRet = ImpConvertAddText(std::move(pRet), bBezier);
2403  }
2404 
2405  return pRet;
2406 }
2407 
2409 {
2410  return 2;
2411 }
2412 
2413 Point SdrEdgeObj::GetSnapPoint(sal_uInt32 i) const
2414 {
2415  const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
2416  sal_uInt16 nCount=pEdgeTrack->GetPointCount();
2417  if (i==0) return (*pEdgeTrack)[0];
2418  else return (*pEdgeTrack)[nCount-1];
2419 }
2420 
2422 {
2423  return false;
2424 }
2425 
2426 sal_uInt32 SdrEdgeObj::GetPointCount() const
2427 {
2428  return 0;
2429 }
2430 
2431 Point SdrEdgeObj::GetPoint(sal_uInt32 i) const
2432 {
2433  const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
2434  sal_uInt16 nCount=pEdgeTrack->GetPointCount();
2435  if (0 == i)
2436  return (*pEdgeTrack)[0];
2437  else
2438  return (*pEdgeTrack)[nCount-1];
2439 }
2440 
2441 void SdrEdgeObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
2442 {
2443  // TODO: Need an implementation to connect differently.
2445  sal_uInt16 nCount=pEdgeTrack->GetPointCount();
2446  if (0 == i)
2447  (*pEdgeTrack)[0]=rPnt;
2448  if (1 == i)
2449  (*pEdgeTrack)[nCount-1]=rPnt;
2451  SetRectsDirty();
2452 }
2453 
2455  : pEdgeTrack(new XPolygon)
2456  , bEdgeTrackDirty(false)
2457  , bEdgeTrackUserDefined(false)
2458 {
2459 }
2460 
2462 {
2463 }
2464 
2466 {
2467  return new SdrEdgeObjGeoData;
2468 }
2469 
2471 {
2473  SdrEdgeObjGeoData& rEGeo=static_cast<SdrEdgeObjGeoData&>(rGeo);
2474  rEGeo.aCon1 =aCon1;
2475  rEGeo.aCon2 =aCon2;
2476  *rEGeo.pEdgeTrack =*pEdgeTrack;
2479  rEGeo.aEdgeInfo =aEdgeInfo;
2480 }
2481 
2483 {
2485  const SdrEdgeObjGeoData& rEGeo=static_cast<const SdrEdgeObjGeoData&>(rGeo);
2486  if (aCon1.pObj!=rEGeo.aCon1.pObj) {
2487  if (aCon1.pObj!=nullptr) aCon1.pObj->RemoveListener(*this);
2488  aCon1=rEGeo.aCon1;
2489  if (aCon1.pObj!=nullptr) aCon1.pObj->AddListener(*this);
2490  }
2491  else
2492  aCon1=rEGeo.aCon1;
2493 
2494  if (aCon2.pObj!=rEGeo.aCon2.pObj) {
2495  if (aCon2.pObj!=nullptr) aCon2.pObj->RemoveListener(*this);
2496  aCon2=rEGeo.aCon2;
2497  if (aCon2.pObj!=nullptr) aCon2.pObj->AddListener(*this);
2498  }
2499  else
2500  aCon2=rEGeo.aCon2;
2501 
2502  *pEdgeTrack =*rEGeo.pEdgeTrack;
2505  aEdgeInfo =rEGeo.aEdgeInfo;
2506 }
2507 
2508 Point SdrEdgeObj::GetTailPoint( bool bTail ) const
2509 {
2510  if( pEdgeTrack && pEdgeTrack->GetPointCount()!=0)
2511  {
2512  const XPolygon& rTrack0 = *pEdgeTrack;
2513  if(bTail)
2514  {
2515  return rTrack0[0];
2516  }
2517  else
2518  {
2519  const sal_uInt16 nSiz = rTrack0.GetPointCount() - 1;
2520  return rTrack0[nSiz];
2521  }
2522  }
2523  else
2524  {
2525  if(bTail)
2526  return aOutRect.TopLeft();
2527  else
2528  return aOutRect.BottomRight();
2529  }
2530 
2531 }
2532 
2533 void SdrEdgeObj::SetTailPoint( bool bTail, const Point& rPt )
2534 {
2535  ImpSetTailPoint( bTail, rPt );
2536  SetChanged();
2537 }
2538 
2544 void SdrEdgeObj::setGluePointIndex( bool bTail, sal_Int32 nIndex /* = -1 */ )
2545 {
2546  tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
2547 
2548  SdrObjConnection& rConn1 = GetConnection( bTail );
2549 
2550  rConn1.SetAutoVertex( nIndex >= 0 && nIndex <= 3 );
2551  rConn1.SetBestConnection( nIndex < 0 );
2552  rConn1.SetBestVertex( nIndex < 0 );
2553 
2554  if( nIndex > 3 )
2555  {
2556  nIndex -= 3; // the start api index is 0, whereas the implementation in svx starts from 1
2557 
2558  // for user defined glue points we have
2559  // to get the id for this index first
2560  const SdrGluePointList* pList = rConn1.GetObject() ? rConn1.GetObject()->GetGluePointList() : nullptr;
2561  if( pList == nullptr || SDRGLUEPOINT_NOTFOUND == pList->FindGluePoint(static_cast<sal_uInt16>(nIndex)) )
2562  return;
2563  }
2564  else if( nIndex < 0 )
2565  {
2566  nIndex = 0;
2567  }
2568 
2569  rConn1.SetConnectorId( static_cast<sal_uInt16>(nIndex) );
2570 
2571  SetChanged();
2572  SetRectsDirty();
2574 }
2575 
2578 sal_Int32 SdrEdgeObj::getGluePointIndex( bool bTail )
2579 {
2580  SdrObjConnection& rConn1 = GetConnection( bTail );
2581  sal_Int32 nId = -1;
2582  if( !rConn1.IsBestConnection() )
2583  {
2584  nId = rConn1.GetConnectorId();
2585  if( !rConn1.IsAutoVertex() )
2586  nId += 3; // the start api index is 0, whereas the implementation in svx starts from 1
2587  }
2588  return nId;
2589 }
2590 
2591 // Implementation was missing; edge track needs to be invalidated additionally.
2593 {
2594  // call parent functionality
2596 
2597  // Additionally, invalidate edge track
2599 }
2600 
2602 {
2603  // use base method from SdrObject, it's not rotatable and
2604  // a call to GetSnapRect() is used. That's what we need for Connector.
2605  return SdrObject::TRGetBaseGeometry(rMatrix, rPolyPolygon);
2606 }
2607 
2609 {
2610  // where appropriate take care for existing connections. For now, just use the
2611  // implementation from SdrObject.
2612  SdrObject::TRSetBaseGeometry(rMatrix, rPolyPolygon);
2613 }
2614 
2615 // for geometry access
2617 {
2618  if(bEdgeTrackDirty)
2619  {
2620  const_cast< SdrEdgeObj* >(this)->ImpRecalcEdgeTrack();
2621  }
2622 
2623  if(pEdgeTrack)
2624  {
2625  return pEdgeTrack->getB2DPolygon();
2626  }
2627  else
2628  {
2629  return ::basegfx::B2DPolygon();
2630  }
2631 }
2632 
2633 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
virtual void ClearObjectItemDirect(const sal_uInt16 nWhich)=0
long Width() const
virtual void NbcSetAnchorPos(const Point &rPnt) override
Definition: svdoedge.cxx:2592
long ImpGetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon &rXP) const
Definition: svdoedge.cxx:135
virtual OUString TakeObjNameSingul() const override
Definition: svdoedge.cxx:1669
Point aMiddleLine
Definition: svdoedge.hxx:84
Point aObj2Line2
Definition: svdoedge.hxx:82
#define SDRATTR_EDGELINE1DELTA
Definition: svddef.hxx:231
virtual bool BegCreate(SdrDragStat &rStat) override
Every object must be able to create itself interactively.
Definition: svdoedge.cxx:2060
SfxHintId
virtual void NbcResize(const Point &rRef, const Fraction &xFact, const Fraction &yFact) override
Definition: svdotxtr.cxx:106
void ShearXPoly(XPolygon &rPoly, const Point &rRef, double tn, bool bVShear)
Definition: svdtrans.cxx:153
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
Utility class SdrObjConnection.
Definition: svdoedge.hxx:38
SdrPageView * GetPageView() const
Definition: svddrag.hxx:88
SdrHintKind GetKind() const
Definition: svdmodel.hxx:124
virtual SdrGluePoint GetCornerGluePoint(sal_uInt16 nNum) const override
Definition: svdoedge.cxx:439
virtual const tools::Rectangle & GetCurrentBoundRect() const
Definition: svdobj.cxx:881
void ImpRecalcEdgeTrack()
Definition: svdoedge.cxx:549
virtual void SetObjectItemDirect(const SfxPoolItem &rItem)=0
virtual ~SdrEdgeObj() override
Definition: svdoedge.cxx:176
void ShearPoint(Point &rPnt, const Point &rRef, double tn, bool bVShear=false)
Definition: svdtrans.hxx:122
void SetAutoVertex(bool rB)
Definition: svdoedge.hxx:61
#define SDRATTR_EDGENODE2HORZDIST
Definition: svddef.hxx:226
SdrObjUserCall * pUserCall
Definition: svdobj.hxx:923
virtual const SdrGluePointList * GetGluePointList() const override
Definition: svdoedge.cxx:444
long AdjustX(long nHorzMove)
virtual bool IsPolyObj() const override
Definition: svdoedge.cxx:2421
SdrObjConnection aCon2
Definition: svdoedge.hxx:115
void ImpSetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon &rXP, long nVal)
Definition: svdoedge.cxx:128
::basegfx::B2DPolygon getEdgeTrack() const
Definition: svdoedge.cxx:2616
long Height() const
const Point & GetStart() const
Definition: svddrag.hxx:92
SdrEscapeDirection
Definition: svdglue.hxx:35
SdrView & GetView()
Definition: svdpagv.hxx:137
int n1
virtual SdrObjectUniquePtr DoConvertToPolyObj(bool bBezier, bool bAddText) const override
Definition: svdoedge.cxx:2394
sal_uInt16 nNotifyingCount
Definition: svdoedge.hxx:145
void ResizeXPoly(XPolygon &rPoly, const Point &rRef, const Fraction &xFact, const Fraction &yFact)
Definition: svdtrans.cxx:71
long AdjustLeft(long nHorzMoveDelta)
sal_uInt16 nMiddleLine
Definition: svdoedge.hxx:91
Utility class SdrEdgeInfoRec.
Definition: svdoedge.hxx:74
sal_uInt16 nObj2Lines
Definition: svdoedge.hxx:90
virtual basegfx::B2DPolyPolygon TakeCreatePoly(const SdrDragStat &rDrag) const override
Polygon dragged by the user when creating the object.
Definition: svdoedge.cxx:2120
bool ImpCanConvTextToCurve() const
Definition: svdotxtr.cxx:428
bool IsHorzDrag() const
Definition: svdhdl.cxx:1701
SdrMetricItem makeSdrEdgeLine3DeltaItem(long nVal)
Definition: sxelditm.hxx:42
std::unique_ptr< XPolygon > pEdgeTrack
Definition: svdoedge.hxx:116
double getX() const
sal_Int64 n
#define SDRATTR_EDGEKIND
Definition: svddef.hxx:223
SdrObject * GetObj(size_t nNum) const
Definition: svdpage.cxx:773
bool IsInserted() const
Definition: svdobj.hxx:792
size_t GetObjCount() const
Definition: svdpage.cxx:767
sal_uInt32 GetPointNum() const
Definition: svdhdl.hxx:222
virtual void SetBoundRectDirty()
Definition: svdobj.cxx:336
bool bEdgeTrackUserDefined
Definition: svdoedge.hxx:118
virtual bool MovCreate(SdrDragStat &rStat) override
Definition: svdoedge.cxx:2074
sal_Int16 nId
void ImpSetEdgeInfoToAttr()
Definition: svdoedge.cxx:266
virtual bool TRGetBaseGeometry(basegfx::B2DHomMatrix &rMatrix, basegfx::B2DPolyPolygon &rPolyPolygon) const override
Definition: svdoedge.cxx:2601
SdrEdgeInfoRec aEdgeInfo
Definition: svdoedge.hxx:146
double getY() const
SdrObject * GetObj() const
Definition: svdhdl.hxx:203
SdrObjectUniquePtr ImpConvertAddText(SdrObjectUniquePtr pObj, bool bBezier) const
Definition: svdotxtr.cxx:467
virtual void NbcSetSnapRect(const tools::Rectangle &rRect) override
Definition: svdoedge.cxx:2250
const SfxBroadcaster * GetBroadcaster() const
Definition: svdobj.cxx:654
OLE object.
Definition: svdobj.hxx:138
void ResetVars()
Definition: svdoedge.cxx:53
virtual OUString TakeObjNamePlural() const override
Definition: svdoedge.cxx:1684
virtual std::unique_ptr< sdr::properties::BaseProperties > CreateObjectSpecificProperties() override
Definition: svdoedge.cxx:147
sal_uInt16 GetCount() const
Definition: svdglue.hxx:126
virtual sdr::properties::BaseProperties & GetProperties() const
Definition: svdobj.cxx:230
#define SDRATTR_EDGENODE1HORZDIST
Definition: svddef.hxx:224
virtual const tools::Rectangle & GetSnapRect() const override
Definition: svdoattr.cxx:91
All geometrical data of an arbitrary object for use in undo/redo.
Definition: svdobj.hxx:226
bool bEdgeTrackUserDefined
Definition: svdoedge.hxx:149
void AddListener(SfxListener &rListener)
Definition: svdobj.cxx:633
SfxHintId GetId() const
basegfx::B2DPolygon ImplAddConnectorOverlay(SdrDragMethod &rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
Definition: svdoedge.cxx:1997
virtual bool HasText() const override
Definition: svdotxat.cxx:429
#define SDRATTR_EDGENODE2VERTDIST
Definition: svddef.hxx:227
void ConnectToNode(bool bTail1, SdrObject *pObj) override
Definition: svdoedge.cxx:454
virtual std::unique_ptr< sdr::contact::ViewContact > CreateObjectSpecificViewContact() override
Definition: svdoedge.cxx:155
virtual void BrkCreate(SdrDragStat &rStat) override
Definition: svdoedge.cxx:2113
bool bEdgeTrackDirty
Definition: svdoedge.hxx:148
long AdjustBottom(long nVertMoveDelta)
void Move(long nHorzMoveDelta, long nVertMoveDelta)
bool bSnapRectDirty
Definition: svdobj.hxx:928
virtual void NbcMirror(const Point &rRef1, const Point &rRef2) override
Definition: svdotxtr.cxx:236
virtual const tools::Rectangle & GetSnapRect() const
Definition: svdobj.cxx:1624
void ImpUndirtyEdgeTrack()
Definition: svdoedge.cxx:541
bool IsVisible() const
Definition: svdobj.hxx:804
bool IsEmpty() const
sal_uInt16 GetConnectorId() const
Definition: svdoedge.hxx:66
sal_uInt16 GetId() const
Definition: svdglue.hxx:86
Provides information about various ZObject properties.
Definition: svdobj.hxx:248
const SdrHdl * GetHdl() const
Definition: svddrag.hxx:101
#define SDRATTR_EDGELINE2DELTA
Definition: svddef.hxx:232
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
basegfx::B2DPolyPolygon GetEdgeTrackPath() const
Definition: svdoedge.cxx:1726
void ImpSetTailPoint(bool bTail1, const Point &rPt)
Definition: svdoedge.cxx:518
long Right() const
int n2
Point aObj2Line3
Definition: svdoedge.hxx:83
virtual basegfx::B2DHomMatrix getCurrentTransformation()
Definition: svddrgmt.cxx:667
SdrPage * getSdrPageFromSdrObject() const
Definition: svdobj.cxx:289
virtual SdrGluePoint GetVertexGluePoint(sal_uInt16 nNum) const
Definition: svdobj.cxx:2188
OUString SvxResId(const char *pId)
Definition: dialmgr.cxx:28
void setX(long nX)
virtual PointerStyle GetCreatePointer() const override
get the cursor/pointer that signals creating this object
Definition: svdoedge.cxx:2127
#define X
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdotext.cxx:1440
int nCount
const SfxItemSet & GetObjectItemSet() const
Definition: svdobj.cxx:1906
virtual void NbcSetPoint(const Point &rPnt, sal_uInt32 i) override
Definition: svdoedge.cxx:2441
SdrObject * SdrObjectPrimitiveHit(const SdrObject &rObject, const Point &rPnt, sal_uInt16 nTol, const SdrPageView &rSdrPageView, const SdrLayerIDSet *pVisiLayer, bool bTextOnly, drawinglayer::primitive2d::Primitive2DContainer *pHitContainer)
tools::Rectangle maRect
Definition: svdotext.hxx:182
SdrEdgeObj & operator=(const SdrEdgeObj &rObj)
Definition: svdoedge.cxx:1654
void setGluePointIndex(bool bTail, sal_Int32 nId=-1)
this method is used by the api to set a glue point for a connection nId == -1 : The best default poin...
Definition: svdoedge.cxx:2544
bool bClosedObj
Definition: svdobj.hxx:941
void ImpSetAttrToEdgeInfo()
Definition: svdoedge.cxx:196
virtual void NbcRotate(const Point &rRef, long nAngle, double sn, double cs) override
Definition: svdoedge.cxx:2302
long Top() const
SdrObjConnection aCon1
Definition: svdoedge.hxx:114
OutputDevice * GetFirstOutputDevice() const
Definition: svdpntv.cxx:93
bool TakeGluePoint(SdrGluePoint &rGP) const
Definition: svdoedge.cxx:63
Utility class SdrEdgeObjGeoData.
Definition: svdoedge.hxx:111
tools::Rectangle maSnapRect
Definition: svdoattr.hxx:42
void setY(long nY)
SdrObject * GetObject() const
Definition: svdoedge.hxx:67
#define XPOLY_APPEND
Definition: xpoly.hxx:37
SdrEdgeLineCode
Definition: svdoedge.hxx:71
B2IRange fround(const B2DRange &rRange)
bool ImpIsHorzLine(SdrEdgeLineCode eLineCode, const XPolygon &rXP) const
Definition: svdoedge.cxx:116
SdrEdgeObj(SdrModel &rSdrModel)
Definition: svdoedge.cxx:161
SdrObject * GetConnectedNode(bool bTail1) const override
Definition: svdoedge.cxx:478
virtual SdrEdgeObj * CloneSdrObject(SdrModel &rTargetModel) const override
Definition: svdoedge.cxx:1649
virtual const tools::Rectangle & GetCurrentBoundRect() const override
Definition: svdoedge.cxx:381
virtual bool BckCreate(SdrDragStat &rStat) override
Definition: svdoedge.cxx:2105
SdrEscapeDirection GetEscDir() const
Definition: svdglue.hxx:84
void SetBestVertex(bool rB)
Definition: svdoedge.hxx:60
SdrObjList * GetObjList() const
Return current List.
Definition: svdpagv.hxx:176
virtual void NbcMirror(const Point &rRef1, const Point &rRef2) override
Definition: svdoedge.cxx:2333
Point BottomRight() const
const SfxPoolItem & GetObjectItem(const sal_uInt16 nWhich) const
Definition: svdobj.cxx:1941
const SdrLayerIDSet & GetVisibleLayers() const
Definition: svdpagv.hxx:217
void SetNoSnap(bool bOn=true)
Definition: svddrag.hxx:121
#define SDRATTR_EDGENODE1VERTDIST
Definition: svddef.hxx:225
virtual void handlePageChange(SdrPage *pOldPage, SdrPage *pNewPage) override
Definition: svdotext.cxx:459
void SetEndDragChangesGeoAndAttributes(bool bOn)
Definition: svddrag.hxx:135
virtual const SdrGluePointList * GetGluePointList() const
Definition: svdobj.cxx:2225
bool mbSuppressed
Definition: svdoedge.hxx:164
virtual void TakeUnrotatedSnapRect(tools::Rectangle &rRect) const override
Definition: svdoedge.cxx:406
virtual SdrGluePointList * ForceGluePointList() override
Definition: svdoedge.cxx:449
SdrEdgeLineCode GetLineCode() const
Definition: svdhdl.hxx:397
void MirrorXPoly(XPolygon &rPoly, const Point &rRef1, const Point &rRef2)
Definition: svdtrans.cxx:137
long AdjustY(long nVertMove)
#define SDRGLUEPOINT_NOTFOUND
Definition: svdglue.hxx:116
virtual Point GetPoint(sal_uInt32 i) const override
Definition: svdoedge.cxx:2431
#define SDRATTR_EDGELINE3DELTA
Definition: svddef.hxx:233
Point GetTailPoint(bool bTail) const
Definition: svdoedge.cxx:2508
OUString sName
SdrObject * GetCreateObj() const
Definition: svdcrtv.hxx:119
sal_uInt16 nObj1Lines
Definition: svdoedge.hxx:89
virtual void handlePageChange(SdrPage *pOldPage, SdrPage *pNewPage) override
Definition: svdoedge.cxx:182
virtual void TRSetBaseGeometry(const basegfx::B2DHomMatrix &rMatrix, const basegfx::B2DPolyPolygon &rPolyPolygon)
Definition: svdobj.cxx:2936
tools::Rectangle aOutRect
Definition: svdobj.hxx:921
void DisconnectFromNode(bool bTail1) override
Definition: svdoedge.cxx:469
SdrPathObjUniquePtr ImpConvertMakeObj(const basegfx::B2DPolyPolygon &rPolyPolygon, bool bClosed, bool bBezier) const
Definition: svdotxtr.cxx:433
int i
void ImpDirtyEdgeTrack()
Definition: svdoedge.cxx:535
sal_Int32 GetPointCount() const
Definition: svddrag.hxx:91
void MirrorPoint(Point &rPnt, const Point &rRef1, const Point &rRef2)
Definition: svdtrans.cxx:103
SdrTextObj & operator=(const SdrTextObj &rObj)
Definition: svdotext.cxx:1015
void ActionChanged() const
Definition: svdobj.cxx:283
SdrView * GetView() const
Definition: svddrag.hxx:86
virtual ~SdrEdgeObjGeoData() override
Definition: svdoedge.cxx:2461
basegfx::B2DPolygon getB2DPolygon() const
Definition: _xpoly.cxx:820
virtual bool hasSpecialDrag() const override
The standard transformations (Move,Resize,Rotate,Mirror,Shear) are taken over by the View (TakeXorPol...
Definition: svdoedge.cxx:1848
SdrEdgeKind
Definition: sxekitm.hxx:26
long Bottom() const
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:299
virtual void NbcResize(const Point &rRefPnt, const Fraction &aXFact, const Fraction &aYFact) override
Definition: svdoedge.cxx:2285
void SetPercent(bool bOn)
Definition: svdglue.hxx:89
SdrObjConnection aCon2
Definition: svdoedge.hxx:142
bool IsInside(const Point &rPOINT) const
Point aObj1Line3
Definition: svdoedge.hxx:81
void SetConnectorId(sal_uInt16 nId)
Definition: svdoedge.hxx:62
virtual basegfx::B2DPolyPolygon TakeXorPoly() const override
The Xor-Polygon is required by the View to drag the object.
Definition: svdoedge.cxx:1689
virtual void NbcMove(const Size &rSiz) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdotxtr.cxx:98
void Reformat()
updates edges that are connected to the edges of this object as if the connected objects send a repai...
Definition: svdoedge.cxx:1634
bool IsAutoVertex() const
Definition: svdoedge.hxx:65
void SetEndDragChangesAttributes(bool bOn)
Definition: svddrag.hxx:133
void SetTailPoint(bool bTail, const Point &rPt)
Definition: svdoedge.cxx:2533
virtual bool beginSpecialDrag(SdrDragStat &rDrag) const override
Definition: svdoedge.cxx:1865
virtual sal_uInt32 GetSnapPointCount() const override
snap to special points of an Object (polygon points, center of circle)
Definition: svdoedge.cxx:2408
bool CheckNodeConnection(bool bTail1) const
Definition: svdoedge.cxx:491
static bool ImpFindConnector(const Point &rPt, const SdrPageView &rPV, SdrObjConnection &rCon, const SdrEdgeObj *pThis, OutputDevice *pOut=nullptr)
Definition: svdoedge.cxx:2132
Abstract DrawObject.
Definition: svdobj.hxx:312
void SetBestConnection(bool rB)
Definition: svdoedge.hxx:59
virtual void NbcMove(const Size &aSize) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdoedge.cxx:2279
SdrObjConnection & GetConnection(bool bTail1)
Definition: svdoedge.hxx:197
virtual void TRSetBaseGeometry(const basegfx::B2DHomMatrix &rMatrix, const basegfx::B2DPolyPolygon &rPolyPolygon) override
Definition: svdoedge.cxx:2608
long X() const
Point & ImpGetLineOffsetPoint(SdrEdgeLineCode eLineCode)
Definition: svdoedge.cxx:92
Point GetAbsolutePos(const SdrObject &rObj) const
Definition: svdglue.cxx:47
OUString GetName() const
Definition: svdobj.cxx:725
#define Y
virtual sal_uInt32 GetHdlCount() const override
Via GetHdlCount the number of Handles can be retrieved.
Definition: svdoedge.cxx:1738
long BigMulDiv(long nVal, long nMul, long nDiv)
Definition: svdtrans.cxx:558
virtual SdrObjectUniquePtr getFullDragClone() const override
Definition: svdoedge.cxx:1853
sal_uInt16 FindGluePoint(sal_uInt16 nId) const
Definition: svdglue.cxx:336
SdrMetricItem makeSdrEdgeLine1DeltaItem(long nVal)
Definition: sxelditm.hxx:34
virtual SdrLayerID GetLayer() const
Definition: svdobj.cxx:604
Point PixelToLogic(const Point &rDevicePt) const
SdrEdgeInfoRec aEdgeInfo
Definition: svdoedge.hxx:119
virtual const tools::Rectangle & GetSnapRect() const override
Definition: svdoedge.cxx:391
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
friend class ImpEdgeHdl
Definition: svdoedge.hxx:135
bool IsSet(SdrLayerID a) const
Definition: svdsob.hxx:66
void Insert(sal_uInt16 nPos, const Point &rPt, PolyFlags eFlags)
Definition: _xpoly.cxx:357
sal_uInt16 nConId
Definition: svdoedge.hxx:46
virtual SdrGluePoint GetCornerGluePoint(sal_uInt16 nNum) const
Definition: svdobj.cxx:2209
void RotatePoint(Point &rPnt, const Point &rRef, double sn, double cs)
Definition: svdtrans.hxx:114
virtual SdrObjGeoData * NewGeoData() const override
A derived class must override these 3 methods if it has own geometric data that must be saved for Und...
Definition: svdoedge.cxx:2465
sal_uInt32 count() const
virtual void RestGeoData(const SdrObjGeoData &rGeo) override
Definition: svdotext.cxx:1448
std::unique_ptr< XPolygon > pEdgeTrack
Definition: svdoedge.hxx:144
void Remove(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:381
long AdjustRight(long nHorzMoveDelta)
XPolygon ImpCalcEdgeTrack(const XPolygon &rTrack0, SdrObjConnection &rCon1, SdrObjConnection &rCon2, SdrEdgeInfoRec *pInfo) const
Definition: svdoedge.cxx:701
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
virtual void Notify(SfxBroadcaster &rBC, const SfxHint &rHint) override
Detects when a stylesheet is changed.
Definition: svdoattr.cxx:103
virtual void TakeObjInfo(SdrObjTransformInfoRec &rInfo) const override
Definition: svdoedge.cxx:359
virtual sal_uInt32 GetPointCount() const override
Definition: svdoedge.cxx:2426
virtual void AddToHdlList(SdrHdlList &rHdlList) const override
Definition: svdoedge.cxx:1767
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdoedge.cxx:2470
OUString ImpGetDescriptionStr(const char *pStrCacheID) const
Definition: svdobj.cxx:1065
virtual bool applySpecialDrag(SdrDragStat &rDrag) override
Definition: svdoedge.cxx:1880
void SetFlags(sal_uInt16 nPos, PolyFlags eFlags)
set the flags for the point at the given position
Definition: _xpoly.cxx:464
OUString aName
virtual OUString getSpecialDragComment(const SdrDragStat &rDrag) const override
Definition: svdoedge.cxx:1982
virtual sal_uInt16 GetObjIdentifier() const override
Definition: svdoedge.cxx:376
bool IsBestConnection() const
Definition: svdoedge.hxx:64
void RemoveListener(SfxListener &rListener)
Definition: svdobj.cxx:644
SdrObjConnection aCon1
Definition: svdoedge.hxx:141
long AdjustTop(long nVertMoveDelta)
SdrMetricItem makeSdrEdgeLine2DeltaItem(long nVal)
Definition: sxelditm.hxx:38
virtual void RestGeoData(const SdrObjGeoData &rGeo) override
Definition: svdoedge.cxx:2482
std::unique_ptr< SdrObject, SdrObjectFreeOp > SdrObjectUniquePtr
Definition: svdobj.hxx:114
const Point & GetPos() const
Definition: svdglue.hxx:82
const Point & GetNow() const
Definition: svddrag.hxx:95
long Left() const
static XPolygon ImpCalcObjToCenter(const Point &rStPt, long nEscAngle, const tools::Rectangle &rRect, const Point &rCenter)
Definition: svdoedge.cxx:642
PointerStyle
bool mbBoundRectCalculationRunning
Definition: svdoedge.hxx:160
virtual void SetRectsDirty(bool bNotMyself=false, bool bRecursive=true)
Definition: svdobj.cxx:462
void SetConnectMarker(const SdrObjConnection &rCon)
Definition: svdcrtv.cxx:284
void SetEdgeTrackDirty()
Definition: svdoedge.hxx:210
sal_uInt16 ImpGetPolyIdx(SdrEdgeLineCode eLineCode, const XPolygon &rXP) const
Definition: svdoedge.cxx:104
sal_uInt16 GetMarkHdlSizePixel() const
Definition: svdmrkv.cxx:1940
virtual void NbcShear(const Point &rRef, long nAngle, double tn, bool bVShear) override
Definition: svdoedge.cxx:2364
void SetEdgeTrackPath(const basegfx::B2DPolyPolygon &rPoly)
Definition: svdoedge.cxx:1706
virtual void Notify(SfxBroadcaster &rBC, const SfxHint &rHint) override
Detects when a stylesheet is changed.
Definition: svdoedge.cxx:1587
void RotateXPoly(XPolygon &rPoly, const Point &rRef, double sn, double cs)
Definition: svdtrans.cxx:87
sal_Int32 getGluePointIndex(bool bTail)
this method is used by the api to return a glue point id for a connection.
Definition: svdoedge.cxx:2578
static SdrEscapeDirection ImpCalcEscAngle(SdrObject const *pObj, const Point &aPt2)
Definition: svdoedge.cxx:604
virtual void NbcSetAnchorPos(const Point &rPnt)
Definition: svdobj.cxx:1597
bool LineGeometryUsageIsNecessary() const
Definition: svdobj.cxx:989
A SdrPage contains exactly one SdrObjList and a description of the physical page dimensions (size / m...
Definition: svdpage.hxx:366
virtual SdrGluePoint GetVertexGluePoint(sal_uInt16 nNum) const override
Definition: svdoedge.cxx:411
virtual void NbcRotate(const Point &rRef, long nAngle, double sn, double cs) override
Definition: svdotxtr.cxx:190
virtual bool TRGetBaseGeometry(basegfx::B2DHomMatrix &rMatrix, basegfx::B2DPolyPolygon &rPolyPolygon) const
Definition: svdobj.cxx:2909
SdrCreateCmd
Definition: svdtypes.hxx:27
sal_uInt16 GetPointCount() const
Definition: _xpoly.cxx:351
virtual void SetChanged()
Definition: svdobj.cxx:956
#define SDRATTR_EDGELINEDELTACOUNT
Definition: svddef.hxx:230
virtual void NbcShear(const Point &rRef, long nAngle, double tn, bool bVShear) override
Definition: svdotxtr.cxx:214
void MoveXPoly(XPolygon &rPoly, const Size &S)
Definition: svdtrans.cxx:31
bool bIsEdge
Definition: svdobj.hxx:942
Point aObj1Line2
Definition: svdoedge.hxx:80
void SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle &rBoundRect) const
Definition: svdobj.cxx:2676
void SetPos(const Point &rNewPos)
Definition: svdglue.hxx:83
bool GetSuppressDefaultConnect() const
Definition: svdoedge.hxx:169
Point Center() const
void AddHdl(std::unique_ptr< SdrHdl > pHdl)
Definition: svdhdl.cxx:2299
long Y() const
virtual bool EndCreate(SdrDragStat &rStat, SdrCreateCmd eCmd) override
Definition: svdoedge.cxx:2090
void HideConnectMarker()
Definition: svdcrtv.cxx:307
Utility class SdrEdgeObj.
Definition: svdoedge.hxx:128
SdrObject * pObj
Definition: svdoedge.hxx:45
virtual void RecalcSnapRect() override
Snap is not done on the BoundRect but if possible on logic coordinates (i.e.
Definition: svdoedge.cxx:401
virtual Point GetSnapPoint(sal_uInt32 i) const override
Definition: svdoedge.cxx:2413