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