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