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