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=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  OUStringBuffer sName(SvxResId(STR_ObjNameSingulEDGE));
1675 
1676  OUString aName(GetName());
1677  if (!aName.isEmpty())
1678  {
1679  sName.append(' ');
1680  sName.append('\'');
1681  sName.append(aName);
1682  sName.append('\'');
1683  }
1684  return sName.makeStringAndClear();
1685 }
1686 
1688 {
1689  return SvxResId(STR_ObjNamePluralEDGE);
1690 }
1691 
1693 {
1694  basegfx::B2DPolyPolygon aPolyPolygon;
1695 
1696  if (bEdgeTrackDirty)
1697  {
1698  const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
1699  }
1700 
1701  if(pEdgeTrack)
1702  {
1703  aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
1704  }
1705 
1706  return aPolyPolygon;
1707 }
1708 
1710 {
1711  if ( !rPoly.count() )
1712  {
1713  bEdgeTrackDirty = true;
1714  bEdgeTrackUserDefined = false;
1715  }
1716  else
1717  {
1718  *pEdgeTrack = XPolygon( rPoly.getB2DPolygon( 0 ) );
1719  bEdgeTrackDirty = false;
1720  bEdgeTrackUserDefined = true;
1721 
1722  // #i110629# also set aRect and maSnapeRect depending on pEdgeTrack
1723  const tools::Rectangle aPolygonBounds(pEdgeTrack->GetBoundRect());
1724  maRect = aPolygonBounds;
1725  maSnapRect = aPolygonBounds;
1726  }
1727 }
1728 
1730 {
1731  basegfx::B2DPolyPolygon aPolyPolygon;
1732 
1733  if (bEdgeTrackDirty)
1734  const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
1735 
1736  aPolyPolygon.append( pEdgeTrack->getB2DPolygon() );
1737 
1738  return aPolyPolygon;
1739 }
1740 
1741 sal_uInt32 SdrEdgeObj::GetHdlCount() const
1742 {
1743  SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
1744  sal_uInt32 nHdlCnt(0);
1745  sal_uInt32 nPointCount(pEdgeTrack->GetPointCount());
1746 
1747  if(nPointCount)
1748  {
1749  nHdlCnt = 2;
1750  if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
1751  {
1752  sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0 ? aEdgeInfo.nObj1Lines - 1 : 0);
1753  sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0 ? aEdgeInfo.nObj2Lines - 1 : 0);
1754  sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1 : 0);
1755  nHdlCnt += nO1 + nO2 + nM;
1756  }
1757  else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
1758  {
1759  if(GetConnectedNode(true))
1760  nHdlCnt++;
1761 
1762  if(GetConnectedNode(false))
1763  nHdlCnt++;
1764  }
1765  }
1766 
1767  return nHdlCnt;
1768 }
1769 
1771 {
1772  sal_uInt32 nPointCount(pEdgeTrack->GetPointCount());
1773  if (nPointCount==0)
1774  return;
1775 
1776  {
1777  std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*pEdgeTrack)[0],SdrHdlKind::Poly));
1778  if (aCon1.pObj!=nullptr && aCon1.bBestVertex) pHdl->Set1PixMore();
1779  pHdl->SetPointNum(0);
1780  rHdlList.AddHdl(std::move(pHdl));
1781  }
1782  {
1783  std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*pEdgeTrack)[sal_uInt16(nPointCount-1)],SdrHdlKind::Poly));
1784  if (aCon2.pObj!=nullptr && aCon2.bBestVertex) pHdl->Set1PixMore();
1785  pHdl->SetPointNum(1);
1786  rHdlList.AddHdl(std::move(pHdl));
1787  }
1788  {
1789  SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
1790  if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
1791  {
1792  sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0 ? aEdgeInfo.nObj1Lines - 1 : 0);
1793  sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0 ? aEdgeInfo.nObj2Lines - 1 : 0);
1794  sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1 : 0);
1795  for(sal_uInt32 i = 0; i < (nO1 + nO2 + nM); ++i)
1796  {
1797  sal_Int32 nPt(0);
1798  sal_uInt32 nNum = i;
1799  std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(Point(),SdrHdlKind::Poly));
1800  if (nNum<nO1) {
1801  nPt=nNum+1;
1802  if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
1803  if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line3);
1804  } else {
1805  nNum=nNum-nO1;
1806  if (nNum<nO2) {
1807  nPt=nPointCount-3-nNum;
1808  if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
1809  if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line3);
1810  } else {
1811  nNum=nNum-nO2;
1812  if (nNum<nM) {
1813  nPt=aEdgeInfo.nMiddleLine;
1814  pHdl->SetLineCode(SdrEdgeLineCode::MiddleLine);
1815  }
1816  }
1817  }
1818  if (nPt>0) {
1819  Point aPos((*pEdgeTrack)[static_cast<sal_uInt16>(nPt)]);
1820  aPos+=(*pEdgeTrack)[static_cast<sal_uInt16>(nPt)+1];
1821  aPos.setX( aPos.X() / 2 );
1822  aPos.setY( aPos.Y() / 2 );
1823  pHdl->SetPos(aPos);
1824  pHdl->SetPointNum(i + 2);
1825  rHdlList.AddHdl(std::move(pHdl));
1826  }
1827  }
1828  }
1829  else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
1830  {
1831  if(GetConnectedNode(true))
1832  {
1833  Point aPos((*pEdgeTrack)[1]);
1834  std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
1835  pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
1836  pHdl->SetPointNum(2);
1837  rHdlList.AddHdl(std::move(pHdl));
1838  }
1839  if(GetConnectedNode(false))
1840  {
1841  Point aPos((*pEdgeTrack)[2]);
1842  std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
1843  pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
1844  pHdl->SetPointNum(3);
1845  rHdlList.AddHdl(std::move(pHdl));
1846  }
1847  }
1848  }
1849 }
1850 
1852 {
1853  return true;
1854 }
1855 
1857 {
1858  // use Clone operator
1860 
1861  // copy connections for clone, SdrEdgeObj::operator= does not do this
1862  pRetval->ConnectToNode(true, GetConnectedNode(true));
1863  pRetval->ConnectToNode(false, GetConnectedNode(false));
1864 
1865  return SdrObjectUniquePtr(pRetval);
1866 }
1867 
1869 {
1870  if(!rDrag.GetHdl())
1871  return false;
1872 
1873  rDrag.SetEndDragChangesAttributes(true);
1874 
1875  if(rDrag.GetHdl()->GetPointNum() < 2)
1876  {
1877  rDrag.SetNoSnap();
1878  }
1879 
1880  return true;
1881 }
1882 
1884 {
1885  SdrEdgeObj* pOriginalEdge = dynamic_cast< SdrEdgeObj* >(rDragStat.GetHdl()->GetObj());
1886  const bool bOriginalEdgeModified(pOriginalEdge == this);
1887 
1888  if(!bOriginalEdgeModified && pOriginalEdge)
1889  {
1890  // copy connections when clone is modified. This is needed because
1891  // as preparation to this modification the data from the original object
1892  // was copied to the clone using the operator=. As can be seen there,
1893  // that operator does not copy the connections (for good reason)
1894  ConnectToNode(true, pOriginalEdge->GetConnection(true).GetObject());
1895  ConnectToNode(false, pOriginalEdge->GetConnection(false).GetObject());
1896  }
1897 
1898  if(rDragStat.GetHdl()->GetPointNum() < 2)
1899  {
1900  // start or end point connector drag
1901  const bool bDragA(0 == rDragStat.GetHdl()->GetPointNum());
1902  const Point aPointNow(rDragStat.GetNow());
1903 
1904  rDragStat.SetEndDragChangesGeoAndAttributes(true);
1905 
1906  if(rDragStat.GetPageView())
1907  {
1908  SdrObjConnection* pDraggedOne(bDragA ? &aCon1 : &aCon2);
1909 
1910  // clear connection
1911  DisconnectFromNode(bDragA);
1912 
1913  // look for new connection
1914  ImpFindConnector(aPointNow, *rDragStat.GetPageView(), *pDraggedOne, pOriginalEdge);
1915 
1916  if(pDraggedOne->pObj)
1917  {
1918  // if found, officially connect to it; ImpFindConnector only
1919  // sets pObj hard
1920  SdrObject* pNewConnection = pDraggedOne->pObj;
1921  pDraggedOne->pObj = nullptr;
1922  ConnectToNode(bDragA, pNewConnection);
1923  }
1924 
1925  if(rDragStat.GetView() && !bOriginalEdgeModified)
1926  {
1927  // show IA helper, but only do this during IA, so not when the original
1928  // Edge gets modified in the last call
1929  rDragStat.GetView()->SetConnectMarker(*pDraggedOne);
1930  }
1931  }
1932 
1933  if(pEdgeTrack)
1934  {
1935  // change pEdgeTrack to modified position
1936  if(bDragA)
1937  {
1938  (*pEdgeTrack)[0] = aPointNow;
1939  }
1940  else
1941  {
1942  (*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount()-1)] = aPointNow;
1943  }
1944  }
1945 
1946  // reset edge info's offsets, this is an end point drag
1952  }
1953  else
1954  {
1955  // control point connector drag
1956  const ImpEdgeHdl* pEdgeHdl = static_cast<const ImpEdgeHdl*>(rDragStat.GetHdl());
1957  const SdrEdgeLineCode eLineCode = pEdgeHdl->GetLineCode();
1958  const Point aDist(rDragStat.GetNow() - rDragStat.GetStart());
1959  sal_Int32 nDist(pEdgeHdl->IsHorzDrag() ? aDist.X() : aDist.Y());
1960 
1961  nDist += aEdgeInfo.ImpGetLineOffset(eLineCode, *pEdgeTrack);
1962  aEdgeInfo.ImpSetLineOffset(eLineCode, *pEdgeTrack, nDist);
1963  }
1964 
1965  // force recalculation of EdgeTrack
1967  bEdgeTrackDirty=false;
1968 
1969  // save EdgeInfos and mark object as user modified
1971  bEdgeTrackUserDefined = false;
1972 
1973  SetRectsDirty();
1974 
1975  if(bOriginalEdgeModified && rDragStat.GetView())
1976  {
1977  // hide connect marker helper again when original gets changed.
1978  // This happens at the end of the interaction
1979  rDragStat.GetView()->HideConnectMarker();
1980  }
1981 
1982  return true;
1983 }
1984 
1986 {
1987  const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
1988 
1989  if(bCreateComment)
1990  {
1991  return OUString();
1992  }
1993  else
1994  {
1995  return ImpGetDescriptionStr(STR_DragEdgeTail);
1996  }
1997 }
1998 
1999 
2000 basegfx::B2DPolygon SdrEdgeObj::ImplAddConnectorOverlay(SdrDragMethod& rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
2001 {
2002  basegfx::B2DPolygon aResult;
2003 
2004  if(bDetail)
2005  {
2006  SdrObjConnection aMyCon1(aCon1);
2007  SdrObjConnection aMyCon2(aCon2);
2008 
2009  if (bTail1)
2010  {
2011  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon1.aObjOfs.X(), aMyCon1.aObjOfs.Y()));
2012  aMyCon1.aObjOfs.setX( basegfx::fround(aTemp.getX()) );
2013  aMyCon1.aObjOfs.setY( basegfx::fround(aTemp.getY()) );
2014  }
2015 
2016  if (bTail2)
2017  {
2018  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon2.aObjOfs.X(), aMyCon2.aObjOfs.Y()));
2019  aMyCon2.aObjOfs.setX( basegfx::fround(aTemp.getX()) );
2020  aMyCon2.aObjOfs.setY( basegfx::fround(aTemp.getY()) );
2021  }
2022 
2023  SdrEdgeInfoRec aInfo(aEdgeInfo);
2024  XPolygon aXP(ImpCalcEdgeTrack(*pEdgeTrack, aMyCon1, aMyCon2, &aInfo));
2025 
2026  if(aXP.GetPointCount())
2027  {
2028  aResult = aXP.getB2DPolygon();
2029  }
2030  }
2031  else
2032  {
2033  Point aPt1((*pEdgeTrack)[0]);
2034  Point aPt2((*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount() - 1)]);
2035 
2036  if (aCon1.pObj && (aCon1.bBestConn || aCon1.bBestVertex))
2037  aPt1 = aCon1.pObj->GetSnapRect().Center();
2038 
2039  if (aCon2.pObj && (aCon2.bBestConn || aCon2.bBestVertex))
2040  aPt2 = aCon2.pObj->GetSnapRect().Center();
2041 
2042  if (bTail1)
2043  {
2044  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
2045  aPt1.setX( basegfx::fround(aTemp.getX()) );
2046  aPt1.setY( basegfx::fround(aTemp.getY()) );
2047  }
2048 
2049  if (bTail2)
2050  {
2051  const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
2052  aPt2.setX( basegfx::fround(aTemp.getX()) );
2053  aPt2.setY( basegfx::fround(aTemp.getY()) );
2054  }
2055 
2056  aResult.append(basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
2057  aResult.append(basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
2058  }
2059 
2060  return aResult;
2061 }
2062 
2064 {
2065  rDragStat.SetNoSnap();
2066  pEdgeTrack->SetPointCount(2);
2067  (*pEdgeTrack)[0]=rDragStat.GetStart();
2068  (*pEdgeTrack)[1]=rDragStat.GetNow();
2069  if (rDragStat.GetPageView()!=nullptr) {
2070  ImpFindConnector(rDragStat.GetStart(),*rDragStat.GetPageView(),aCon1,this);
2071  ConnectToNode(true,aCon1.pObj);
2072  }
2074  return true;
2075 }
2076 
2078 {
2079  sal_uInt16 nMax=pEdgeTrack->GetPointCount();
2080  (*pEdgeTrack)[nMax-1]=rDragStat.GetNow();
2081  if (rDragStat.GetPageView()!=nullptr) {
2082  ImpFindConnector(rDragStat.GetNow(),*rDragStat.GetPageView(),aCon2,this);
2083  rDragStat.GetView()->SetConnectMarker(aCon2);
2084  }
2086  m_bSnapRectDirty=true;
2087  ConnectToNode(false,aCon2.pObj);
2089  bEdgeTrackDirty=false;
2090  return true;
2091 }
2092 
2094 {
2095  bool bOk=(eCmd==SdrCreateCmd::ForceEnd || rDragStat.GetPointCount()>=2);
2096  if (bOk) {
2097  ConnectToNode(true,aCon1.pObj);
2098  ConnectToNode(false,aCon2.pObj);
2099  if (rDragStat.GetView()!=nullptr) {
2100  rDragStat.GetView()->HideConnectMarker();
2101  }
2102  ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
2103  }
2104  SetRectsDirty();
2105  return bOk;
2106 }
2107 
2109 {
2110  if (rDragStat.GetView()!=nullptr) {
2111  rDragStat.GetView()->HideConnectMarker();
2112  }
2113  return false;
2114 }
2115 
2117 {
2118  if (rDragStat.GetView()!=nullptr) {
2119  rDragStat.GetView()->HideConnectMarker();
2120  }
2121 }
2122 
2124 {
2125  basegfx::B2DPolyPolygon aRetval;
2126  aRetval.append(pEdgeTrack->getB2DPolygon());
2127  return aRetval;
2128 }
2129 
2131 {
2132  return PointerStyle::DrawConnect;
2133 }
2134 
2135 bool SdrEdgeObj::ImpFindConnector(const Point& rPt, const SdrPageView& rPV, SdrObjConnection& rCon, const SdrEdgeObj* pThis, OutputDevice* pOut)
2136 {
2137  rCon.ResetVars();
2138  if (pOut==nullptr) pOut=rPV.GetView().GetFirstOutputDevice();
2139  if (pOut==nullptr) return false;
2140  SdrObjList* pOL=rPV.GetObjList();
2141  const SdrLayerIDSet& rVisLayer=rPV.GetVisibleLayers();
2142  // sensitive area of connectors is twice as large as the one of the handles
2143  sal_uInt16 nMarkHdSiz=rPV.GetView().GetMarkHdlSizePixel();
2144  Size aHalfConSiz(nMarkHdSiz,nMarkHdSiz);
2145  if (comphelper::LibreOfficeKit::isActive() && pOut->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
2146  aHalfConSiz=pOut->PixelToLogic(aHalfConSiz, MapMode(MapUnit::Map100thMM));
2147  else
2148  aHalfConSiz=pOut->PixelToLogic(aHalfConSiz);
2149  tools::Rectangle aMouseRect(rPt,rPt);
2150  aMouseRect.AdjustLeft( -(aHalfConSiz.Width()) );
2151  aMouseRect.AdjustTop( -(aHalfConSiz.Height()) );
2152  aMouseRect.AdjustRight(aHalfConSiz.Width() );
2153  aMouseRect.AdjustBottom(aHalfConSiz.Height() );
2154  sal_uInt16 nBoundHitTol=static_cast<sal_uInt16>(aHalfConSiz.Width())/2; if (nBoundHitTol==0) nBoundHitTol=1;
2155  size_t no=pOL->GetObjCount();
2156  bool bFnd = false;
2157  SdrObjConnection aTestCon;
2158 
2159  while (no>0 && !bFnd) {
2160  // issue: group objects on different layers return LayerID=0!
2161  no--;
2162  SdrObject* pObj=pOL->GetObj(no);
2163  if (rVisLayer.IsSet(pObj->GetLayer()) && pObj->IsVisible() && // only visible objects
2164  (pThis==nullptr || pObj!=static_cast<SdrObject const *>(pThis))) // don't connect it to itself
2165  {
2166  tools::Rectangle aObjBound(pObj->GetCurrentBoundRect());
2167  if (aObjBound.IsOver(aMouseRect)) {
2168  aTestCon.ResetVars();
2169  bool bEdge=dynamic_cast<const SdrEdgeObj *>(pObj) != nullptr; // no BestCon for Edge
2170  // User-defined connectors have absolute priority.
2171  // After those come Vertex, Corner and center (Best), all prioritized equally.
2172  // Finally, a HitTest for the object.
2173  const SdrGluePointList* pGPL=pObj->GetGluePointList();
2174  sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
2175  sal_uInt16 nGesAnz=nGluePointCnt+9;
2176  bool bUserFnd = false;
2177  sal_uIntPtr nBestDist=0xFFFFFFFF;
2178  for (sal_uInt16 i=0; i<nGesAnz; i++)
2179  {
2180  bool bUser=i<nGluePointCnt;
2181  bool bVertex=i>=nGluePointCnt+0 && i<nGluePointCnt+4;
2182  bool bCorner=i>=nGluePointCnt+4 && i<nGluePointCnt+8;
2183  bool bCenter=i==nGluePointCnt+8;
2184  bool bOk = false;
2185  Point aConPos;
2186  sal_uInt16 nConNum=i;
2187  if (bUser) {
2188  const SdrGluePoint& rGP=(*pGPL)[nConNum];
2189  aConPos=rGP.GetAbsolutePos(*pObj);
2190  nConNum=rGP.GetId();
2191  bOk = true;
2192  } else if (bVertex && !bUserFnd) {
2193  nConNum=nConNum-nGluePointCnt;
2194  SdrGluePoint aPt(pObj->GetVertexGluePoint(nConNum));
2195  aConPos=aPt.GetAbsolutePos(*pObj);
2196  bOk = true;
2197  } else if (bCorner && !bUserFnd) {
2198  nConNum-=nGluePointCnt+4;
2199  i+=3;
2200  }
2201  else if (bCenter && !bUserFnd && !bEdge)
2202  {
2203  // Suppress default connect at object center
2204  if(!pThis || !pThis->GetSuppressDefaultConnect())
2205  {
2206  // not the edges!
2207  nConNum=0;
2208  aConPos=aObjBound.Center();
2209  bOk = true;
2210  }
2211  }
2212  if (bOk && aMouseRect.IsInside(aConPos)) {
2213  if (bUser) bUserFnd = true;
2214  bFnd = true;
2215  sal_uIntPtr nDist=static_cast<sal_uIntPtr>(std::abs(aConPos.X()-rPt.X()))+static_cast<sal_uIntPtr>(std::abs(aConPos.Y()-rPt.Y()));
2216  if (nDist<nBestDist) {
2217  nBestDist=nDist;
2218  aTestCon.pObj=pObj;
2219  aTestCon.nConId=nConNum;
2220  aTestCon.bAutoCorner=bCorner;
2221  aTestCon.bAutoVertex=bVertex;
2222  aTestCon.bBestConn=false; // bCenter;
2223  aTestCon.bBestVertex=bCenter;
2224  }
2225  }
2226  }
2227  // if no connector is hit, try HitTest again, for BestConnector (=bCenter)
2228  if(!bFnd &&
2229  !bEdge &&
2230  SdrObjectPrimitiveHit(*pObj, rPt, nBoundHitTol, rPV, &rVisLayer, false))
2231  {
2232  // Suppress default connect at object inside bound
2233  if(!pThis || !pThis->GetSuppressDefaultConnect())
2234  {
2235  bFnd = true;
2236  aTestCon.pObj=pObj;
2237  aTestCon.bBestConn=true;
2238  }
2239  }
2240  if (bFnd) {
2241  aMouseRect.AdjustLeft( -nBoundHitTol );
2242  aMouseRect.AdjustTop( -nBoundHitTol );
2243  aMouseRect.AdjustRight(nBoundHitTol );
2244  aMouseRect.AdjustBottom(nBoundHitTol );
2245  }
2246 
2247  }
2248  }
2249  }
2250  rCon=aTestCon;
2251  return bFnd;
2252 }
2253 
2255 {
2256  const tools::Rectangle aOld(GetSnapRect());
2257 
2258  if(aOld == rRect)
2259  return;
2260 
2261  if (maRect.IsEmpty() && 0 == pEdgeTrack->GetPointCount())
2262  {
2263  // #i110629# When initializing, do not scale on empty Rectangle; this
2264  // will mirror the underlying text object (!)
2265  maRect = rRect;
2266  maSnapRect = rRect;
2267  }
2268  else
2269  {
2270  tools::Long nMulX = rRect.Right() - rRect.Left();
2271  tools::Long nDivX = aOld.Right() - aOld.Left();
2272  tools::Long nMulY = rRect.Bottom() - rRect.Top();
2273  tools::Long nDivY = aOld.Bottom() - aOld.Top();
2274  if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2275  if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2276  Fraction aX(nMulX, nDivX);
2277  Fraction aY(nMulY, nDivY);
2278  NbcResize(aOld.TopLeft(), aX, aY);
2279  NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2280  }
2281 }
2282 
2283 void SdrEdgeObj::NbcMove(const Size& rSiz)
2284 {
2285  SdrTextObj::NbcMove(rSiz);
2286  MoveXPoly(*pEdgeTrack,rSiz);
2287 }
2288 
2289 void SdrEdgeObj::NbcResize(const Point& rRefPnt, const Fraction& aXFact, const Fraction& aYFact)
2290 {
2291  SdrTextObj::NbcResize(rRefPnt,aXFact,aXFact);
2292  ResizeXPoly(*pEdgeTrack,rRefPnt,aXFact,aYFact);
2293 
2294  // if resize is not from paste, forget user distances
2295  if (!getSdrModelFromSdrObject().IsPasteResize())
2296  {
2302  }
2303 }
2304 
2305 // #i54102# added rotation support
2306 void SdrEdgeObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
2307 {
2309  {
2310  // #i120437# special handling when track is imported, apply
2311  // transformation directly to imported track.
2312  SdrTextObj::NbcRotate(rRef, nAngle, sn, cs);
2313  RotateXPoly(*pEdgeTrack, rRef, sn, cs);
2314  }
2315  else
2316  {
2317  // handle start and end point if not connected
2318  const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2319  const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2320 
2321  if(!bCon1 && pEdgeTrack)
2322  {
2323  RotatePoint((*pEdgeTrack)[0],rRef,sn,cs);
2325  }
2326 
2327  if(!bCon2 && pEdgeTrack)
2328  {
2329  sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
2330  RotatePoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,sn,cs);
2332  }
2333  }
2334 }
2335 
2336 // #i54102# added mirror support
2337 void SdrEdgeObj::NbcMirror(const Point& rRef1, const Point& rRef2)
2338 {
2340  {
2341  // #i120437# special handling when track is imported, apply
2342  // transformation directly to imported track.
2343  SdrTextObj::NbcMirror(rRef1, rRef2);
2344  MirrorXPoly(*pEdgeTrack, rRef1, rRef2);
2345  }
2346  else
2347  {
2348  // handle start and end point if not connected
2349  const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2350  const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2351 
2352  if(!bCon1 && pEdgeTrack)
2353  {
2354  MirrorPoint((*pEdgeTrack)[0],rRef1,rRef2);
2356  }
2357 
2358  if(!bCon2 && pEdgeTrack)
2359  {
2360  sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
2361  MirrorPoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef1,rRef2);
2363  }
2364  }
2365 }
2366 
2367 // #i54102# added shear support
2368 void SdrEdgeObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
2369 {
2371  {
2372  // #i120437# special handling when track is imported, apply
2373  // transformation directly to imported track.
2374  SdrTextObj::NbcShear(rRef, nAngle, tn, bVShear);
2375  ShearXPoly(*pEdgeTrack, rRef, tn, bVShear);
2376  }
2377  else
2378  {
2379  // handle start and end point if not connected
2380  const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2381  const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
2382 
2383  if(!bCon1 && pEdgeTrack)
2384  {
2385  ShearPoint((*pEdgeTrack)[0],rRef,tn,bVShear);
2387  }
2388 
2389  if(!bCon2 && pEdgeTrack)
2390  {
2391  sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
2392  ShearPoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,tn,bVShear);
2394  }
2395  }
2396 }
2397 
2398 SdrObjectUniquePtr SdrEdgeObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
2399 {
2400  basegfx::B2DPolyPolygon aPolyPolygon;
2401  aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
2402  SdrObjectUniquePtr pRet = ImpConvertMakeObj(aPolyPolygon, false, bBezier);
2403 
2404  if(bAddText)
2405  {
2406  pRet = ImpConvertAddText(std::move(pRet), bBezier);
2407  }
2408 
2409  return pRet;
2410 }
2411 
2413 {
2414  return 2;
2415 }
2416 
2417 Point SdrEdgeObj::GetSnapPoint(sal_uInt32 i) const
2418 {
2419  const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
2420  sal_uInt16 nCount=pEdgeTrack->GetPointCount();
2421  if (i==0) return (*pEdgeTrack)[0];
2422  else return (*pEdgeTrack)[nCount-1];
2423 }
2424 
2426 {
2427  return false;
2428 }
2429 
2430 sal_uInt32 SdrEdgeObj::GetPointCount() const
2431 {
2432  return 0;
2433 }
2434 
2435 Point SdrEdgeObj::GetPoint(sal_uInt32 i) const
2436 {
2437  const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
2438  sal_uInt16 nCount=pEdgeTrack->GetPointCount();
2439  if (0 == i)
2440  return (*pEdgeTrack)[0];
2441  else
2442  return (*pEdgeTrack)[nCount-1];
2443 }
2444 
2445 void SdrEdgeObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
2446 {
2447  // TODO: Need an implementation to connect differently.
2449  sal_uInt16 nCount=pEdgeTrack->GetPointCount();
2450  if (0 == i)
2451  (*pEdgeTrack)[0]=rPnt;
2452  if (1 == i)
2453  (*pEdgeTrack)[nCount-1]=rPnt;
2455  SetRectsDirty();
2456 }
2457 
2459  : pEdgeTrack(new XPolygon)
2460  , bEdgeTrackDirty(false)
2461  , bEdgeTrackUserDefined(false)
2462 {
2463 }
2464 
2466 {
2467 }
2468 
2469 std::unique_ptr<SdrObjGeoData> SdrEdgeObj::NewGeoData() const
2470 {
2471  return std::make_unique<SdrEdgeObjGeoData>();
2472 }
2473 
2475 {
2477  SdrEdgeObjGeoData& rEGeo=static_cast<SdrEdgeObjGeoData&>(rGeo);
2478  rEGeo.aCon1 =aCon1;
2479  rEGeo.aCon2 =aCon2;
2480  *rEGeo.pEdgeTrack =*pEdgeTrack;
2483  rEGeo.aEdgeInfo =aEdgeInfo;
2484 }
2485 
2487 {
2489  const SdrEdgeObjGeoData& rEGeo=static_cast<const SdrEdgeObjGeoData&>(rGeo);
2490  if (aCon1.pObj!=rEGeo.aCon1.pObj) {
2491  if (aCon1.pObj!=nullptr) aCon1.pObj->RemoveListener(*this);
2492  aCon1=rEGeo.aCon1;
2493  if (aCon1.pObj!=nullptr) aCon1.pObj->AddListener(*this);
2494  }
2495  else
2496  aCon1=rEGeo.aCon1;
2497 
2498  if (aCon2.pObj!=rEGeo.aCon2.pObj) {
2499  if (aCon2.pObj!=nullptr) aCon2.pObj->RemoveListener(*this);
2500  aCon2=rEGeo.aCon2;
2501  if (aCon2.pObj!=nullptr) aCon2.pObj->AddListener(*this);
2502  }
2503  else
2504  aCon2=rEGeo.aCon2;
2505 
2506  *pEdgeTrack =*rEGeo.pEdgeTrack;
2509  aEdgeInfo =rEGeo.aEdgeInfo;
2510 }
2511 
2512 Point SdrEdgeObj::GetTailPoint( bool bTail ) const
2513 {
2514  if( pEdgeTrack && pEdgeTrack->GetPointCount()!=0)
2515  {
2516  const XPolygon& rTrack0 = *pEdgeTrack;
2517  if(bTail)
2518  {
2519  return rTrack0[0];
2520  }
2521  else
2522  {
2523  const sal_uInt16 nSiz = rTrack0.GetPointCount() - 1;
2524  return rTrack0[nSiz];
2525  }
2526  }
2527  else
2528  {
2529  if(bTail)
2530  return m_aOutRect.TopLeft();
2531  else
2532  return m_aOutRect.BottomRight();
2533  }
2534 
2535 }
2536 
2537 void SdrEdgeObj::SetTailPoint( bool bTail, const Point& rPt )
2538 {
2539  ImpSetTailPoint( bTail, rPt );
2540  SetChanged();
2541 }
2542 
2548 void SdrEdgeObj::setGluePointIndex( bool bTail, sal_Int32 nIndex /* = -1 */ )
2549 {
2550  tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
2551 
2552  SdrObjConnection& rConn1 = GetConnection( bTail );
2553 
2554  rConn1.SetAutoVertex( nIndex >= 0 && nIndex <= 3 );
2555  rConn1.SetBestConnection( nIndex < 0 );
2556  rConn1.SetBestVertex( nIndex < 0 );
2557 
2558  if( nIndex > 3 )
2559  {
2560  nIndex -= 3; // the start api index is 0, whereas the implementation in svx starts from 1
2561 
2562  // for user defined glue points we have
2563  // to get the id for this index first
2564  const SdrGluePointList* pList = rConn1.GetObject() ? rConn1.GetObject()->GetGluePointList() : nullptr;
2565  if( pList == nullptr || SDRGLUEPOINT_NOTFOUND == pList->FindGluePoint(static_cast<sal_uInt16>(nIndex)) )
2566  return;
2567  }
2568  else if( nIndex < 0 )
2569  {
2570  nIndex = 0;
2571  }
2572 
2573  rConn1.SetConnectorId( static_cast<sal_uInt16>(nIndex) );
2574 
2575  SetChanged();
2576  SetRectsDirty();
2578 }
2579 
2582 sal_Int32 SdrEdgeObj::getGluePointIndex( bool bTail )
2583 {
2584  SdrObjConnection& rConn1 = GetConnection( bTail );
2585  sal_Int32 nId = -1;
2586  if( !rConn1.IsBestConnection() )
2587  {
2588  nId = rConn1.GetConnectorId();
2589  if( !rConn1.IsAutoVertex() )
2590  nId += 3; // the start api index is 0, whereas the implementation in svx starts from 1
2591  }
2592  return nId;
2593 }
2594 
2595 // Implementation was missing; edge track needs to be invalidated additionally.
2596 void SdrEdgeObj::NbcSetAnchorPos(const Point& rPnt)
2597 {
2598  // call parent functionality
2600 
2601  // Additionally, invalidate edge track
2603 }
2604 
2606 {
2607  // use base method from SdrObject, it's not rotatable and
2608  // a call to GetSnapRect() is used. That's what we need for Connector.
2609  return SdrObject::TRGetBaseGeometry(rMatrix, rPolyPolygon);
2610 }
2611 
2613 {
2614  // where appropriate take care for existing connections. For now, just use the
2615  // implementation from SdrObject.
2616  SdrObject::TRSetBaseGeometry(rMatrix, rPolyPolygon);
2617 }
2618 
2619 // for geometry access
2621 {
2622  if(bEdgeTrackDirty)
2623  {
2624  const_cast< SdrEdgeObj* >(this)->ImpRecalcEdgeTrack();
2625  }
2626 
2627  if(pEdgeTrack)
2628  {
2629  return pEdgeTrack->getB2DPolygon();
2630  }
2631  else
2632  {
2633  return ::basegfx::B2DPolygon();
2634  }
2635 }
2636 
2637 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
virtual void ClearObjectItemDirect(const sal_uInt16 nWhich)=0
virtual void NbcSetAnchorPos(const Point &rPnt) override
Definition: svdoedge.cxx:2596
virtual OUString TakeObjNameSingul() const override
Definition: svdoedge.cxx:1672
Point aMiddleLine
Definition: svdoedge.hxx:84
Point aObj2Line2
Definition: svdoedge.hxx:82
virtual bool BegCreate(SdrDragStat &rStat) override
Every object must be able to create itself interactively.
Definition: svdoedge.cxx:2063
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:38
SdrPageView * GetPageView() const
Definition: svddrag.hxx:88
SdrHintKind GetKind() const
Definition: svdmodel.hxx:124
virtual SdrGluePoint GetCornerGluePoint(sal_uInt16 nNum) const override
Definition: svdoedge.cxx:456
virtual const tools::Rectangle & GetCurrentBoundRect() const
Definition: svdobj.cxx:931
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:123
tools::Long ImpGetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon &rXP) const
Definition: svdoedge.cxx:131
void SetAutoVertex(bool rB)
Definition: svdoedge.hxx:61
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
virtual const SdrGluePointList * GetGluePointList() const override
Definition: svdoedge.cxx:461
virtual bool IsPolyObj() const override
Definition: svdoedge.cxx:2425
SdrObjConnection aCon2
Definition: svdoedge.hxx:115
bool m_bClosedObj
Definition: svdobj.hxx:892
::basegfx::B2DPolygon getEdgeTrack() const
Definition: svdoedge.cxx:2620
const Point & GetStart() const
Definition: svddrag.hxx:92
SdrEscapeDirection
Definition: svdglue.hxx:36
SdrView & GetView()
Definition: svdpagv.hxx:135
int n1
virtual SdrObjectUniquePtr DoConvertToPolyObj(bool bBezier, bool bAddText) const override
Definition: svdoedge.cxx:2398
sal_uInt16 nNotifyingCount
Definition: svdoedge.hxx:145
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:91
Utility class SdrEdgeInfoRec.
Definition: svdoedge.hxx:74
long Long
sal_uInt16 nObj2Lines
Definition: svdoedge.hxx:90
virtual basegfx::B2DPolyPolygon TakeCreatePoly(const SdrDragStat &rDrag) const override
Polygon dragged by the user when creating the object.
Definition: svdoedge.cxx:2123
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:116
double getX() const
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:88
virtual void SetBoundRectDirty()
Definition: svdobj.cxx:313
bool bEdgeTrackUserDefined
Definition: svdoedge.hxx:118
virtual bool MovCreate(SdrDragStat &rStat) override
Definition: svdoedge.cxx:2077
sal_Int16 nId
void ImpSetEdgeInfoToAttr()
Definition: svdoedge.cxx:283
virtual bool TRGetBaseGeometry(basegfx::B2DHomMatrix &rMatrix, basegfx::B2DPolyPolygon &rPolyPolygon) const override
Definition: svdoedge.cxx:2605
const MapMode & GetMapMode() const
SdrEdgeInfoRec aEdgeInfo
Definition: svdoedge.hxx:146
double getY() const
SdrObject * GetObj() const
Definition: svdhdl.hxx:203
constexpr TypedWhichId< SdrEdgeNode2VertDistItem > SDRATTR_EDGENODE2VERTDIST(SDRATTR_EDGE_FIRST+4)
SdrObjectUniquePtr ImpConvertAddText(SdrObjectUniquePtr pObj, bool bBezier) const
Definition: svdotxtr.cxx:463
virtual void NbcSetSnapRect(const tools::Rectangle &rRect) override
Definition: svdoedge.cxx:2254
const SfxBroadcaster * GetBroadcaster() const
Definition: svdobj.cxx:701
void ResetVars()
Definition: svdoedge.cxx:49
tools::Rectangle m_aOutRect
Definition: svdobj.hxx:872
virtual OUString TakeObjNamePlural() const override
Definition: svdoedge.cxx:1687
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:207
virtual void NbcRotate(const Point &rRef, Degree100 nAngle, double sn, double cs) override
Definition: svdoedge.cxx:2306
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:149
SdrObjKind
Definition: svdobjkind.hxx:24
void AddListener(SfxListener &rListener)
Definition: svdobj.cxx:680
SfxHintId GetId() const
basegfx::B2DPolygon ImplAddConnectorOverlay(SdrDragMethod &rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
Definition: svdoedge.cxx:2000
constexpr TypedWhichId< SdrEdgeLineDeltaCountItem > SDRATTR_EDGELINEDELTACOUNT(SDRATTR_EDGE_FIRST+7)
virtual bool HasText() const override
Definition: svdotxat.cxx:416
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:2116
bool bEdgeTrackDirty
Definition: svdoedge.hxx:148
virtual void NbcMirror(const Point &rRef1, const Point &rRef2) override
Definition: svdotxtr.cxx:232
virtual const tools::Rectangle & GetSnapRect() const
Definition: svdobj.cxx:1645
void ImpUndirtyEdgeTrack()
Definition: svdoedge.cxx:558
bool IsVisible() const
Definition: svdobj.hxx:753
bool IsEmpty() const
bool m_bIsEdge
Definition: svdobj.hxx:893
sal_uInt16 GetConnectorId() const
Definition: svdoedge.hxx:66
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:101
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
constexpr TypedWhichId< SdrEdgeKindItem > SDRATTR_EDGEKIND(SDRATTR_EDGE_FIRST+0)
basegfx::B2DPolyPolygon GetEdgeTrackPath() const
Definition: svdoedge.cxx:1729
void ImpSetTailPoint(bool bTail1, const Point &rPt)
Definition: svdoedge.cxx:535
tools::Long nAngle1
Definition: svdoedge.hxx:87
int n2
Point aObj2Line3
Definition: svdoedge.hxx:83
virtual basegfx::B2DHomMatrix getCurrentTransformation()
Definition: svddrgmt.cxx:660
tools::Long Left() const
SdrPage * getSdrPageFromSdrObject() const
Definition: svdobj.cxx:266
virtual SdrGluePoint GetVertexGluePoint(sal_uInt16 nNum) const
Definition: svdobj.cxx:2230
OUString SvxResId(const char *pId)
Definition: dialmgr.cxx:25
virtual PointerStyle GetCreatePointer() const override
get the cursor/pointer that signals creating this object
Definition: svdoedge.cxx:2130
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdotext.cxx:1443
int nCount
SdrMetricItem makeSdrEdgeLine2DeltaItem(tools::Long nVal)
Definition: sxelditm.hxx:43
tools::Long Bottom() const
const SfxItemSet & GetObjectItemSet() const
Definition: svdobj.cxx:1952
virtual void NbcSetPoint(const Point &rPnt, sal_uInt32 i) override
Definition: svdoedge.cxx:2445
SdrObject * SdrObjectPrimitiveHit(const SdrObject &rObject, const Point &rPnt, sal_uInt16 nTol, const SdrPageView &rSdrPageView, const SdrLayerIDSet *pVisiLayer, bool bTextOnly, drawinglayer::primitive2d::Primitive2DContainer *pHitContainer)
tools::Rectangle maRect
Definition: svdotext.hxx:182
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:2548
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:114
OutputDevice * GetFirstOutputDevice() const
Definition: svdpntv.cxx:88
bool TakeGluePoint(SdrGluePoint &rGP) const
Definition: svdoedge.cxx:59
Utility class SdrEdgeObjGeoData.
Definition: svdoedge.hxx:111
tools::Rectangle maSnapRect
Definition: svdoattr.hxx:42
SdrObject * GetObject() const
Definition: svdoedge.hxx:67
#define XPOLY_APPEND
Definition: xpoly.hxx:38
SdrEdgeLineCode
Definition: svdoedge.hxx:71
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
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
virtual bool BckCreate(SdrDragStat &rStat) override
Definition: svdoedge.cxx:2108
SdrEscapeDirection GetEscDir() const
Definition: svdglue.hxx:85
void SetBestVertex(bool rB)
Definition: svdoedge.hxx:60
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:2469
SdrObjList * GetObjList() const
Return current List.
Definition: svdpagv.hxx:174
virtual void NbcMirror(const Point &rRef1, const Point &rRef2) override
Definition: svdoedge.cxx:2337
constexpr TypedWhichId< SdrMetricItem > SDRATTR_EDGELINE3DELTA(SDRATTR_EDGE_FIRST+10)
Point BottomRight() const
const SfxPoolItem & GetObjectItem(const sal_uInt16 nWhich) const
Definition: svdobj.cxx:1987
constexpr TypedWhichId< SdrEdgeNode2HorzDistItem > SDRATTR_EDGENODE2HORZDIST(SDRATTR_EDGE_FIRST+3)
const SdrLayerIDSet & GetVisibleLayers() const
Definition: svdpagv.hxx:215
void SetNoSnap(bool bOn=true)
Definition: svddrag.hxx:121
virtual void handlePageChange(SdrPage *pOldPage, SdrPage *pNewPage) override
Definition: svdotext.cxx:491
void SetEndDragChangesGeoAndAttributes(bool bOn)
Definition: svddrag.hxx:135
virtual const SdrGluePointList * GetGluePointList() const
Definition: svdobj.cxx:2267
bool mbSuppressed
Definition: svdoedge.hxx:164
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:2435
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:2512
OUString sName
SdrObject * GetCreateObj() const
Definition: svdcrtv.hxx:116
sal_uInt16 nObj1Lines
Definition: svdoedge.hxx:89
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:2979
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:2486
void ImpDirtyEdgeTrack()
Definition: svdoedge.cxx:552
sal_Int32 GetPointCount() const
Definition: svddrag.hxx:91
void MirrorPoint(Point &rPnt, const Point &rRef1, const Point &rRef2)
Definition: svdtrans.cxx:105
void ActionChanged() const
Definition: svdobj.cxx:260
SdrView * GetView() const
Definition: svddrag.hxx:86
virtual ~SdrEdgeObjGeoData() override
Definition: svdoedge.cxx:2465
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:1851
SdrEdgeKind
Definition: sxekitm.hxx:26
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:276
SdrObjUserCall * m_pUserCall
Definition: svdobj.hxx:874
virtual void NbcResize(const Point &rRefPnt, const Fraction &aXFact, const Fraction &aYFact) override
Definition: svdoedge.cxx:2289
void SetPercent(bool bOn)
Definition: svdglue.hxx:90
SdrObjConnection aCon2
Definition: svdoedge.hxx:142
bool IsInside(const Point &rPOINT) const
Point aObj1Line3
Definition: svdoedge.hxx:81
void ImpSetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon &rXP, tools::Long nVal)
Definition: svdoedge.cxx:124
void SetConnectorId(sal_uInt16 nId)
Definition: svdoedge.hxx:62
virtual basegfx::B2DPolyPolygon TakeXorPoly() const override
The Xor-Polygon is required by the View to drag the object.
Definition: svdoedge.cxx:1692
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:65
void SetEndDragChangesAttributes(bool bOn)
Definition: svddrag.hxx:133
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:2537
virtual bool beginSpecialDrag(SdrDragStat &rDrag) const override
Definition: svdoedge.cxx:1868
MapUnit GetMapUnit() const
virtual sal_uInt32 GetSnapPointCount() const override
snap to special points of an Object (polygon points, center of circle)
Definition: svdoedge.cxx:2412
bool CheckNodeConnection(bool bTail1) const
Definition: svdoedge.cxx:508
static bool ImpFindConnector(const Point &rPt, const SdrPageView &rPV, SdrObjConnection &rCon, const SdrEdgeObj *pThis, OutputDevice *pOut=nullptr)
Definition: svdoedge.cxx:2135
tools::Long Top() const
Abstract DrawObject.
Definition: svdobj.hxx:258
void SetBestConnection(bool rB)
Definition: svdoedge.hxx:59
virtual void NbcMove(const Size &aSize) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdoedge.cxx:2283
SdrObjConnection & GetConnection(bool bTail1)
Definition: svdoedge.hxx:199
virtual void TRSetBaseGeometry(const basegfx::B2DHomMatrix &rMatrix, const basegfx::B2DPolyPolygon &rPolyPolygon) override
Definition: svdoedge.cxx:2612
Point & ImpGetLineOffsetPoint(SdrEdgeLineCode eLineCode)
Definition: svdoedge.cxx:88
Point GetAbsolutePos(const SdrObject &rObj) const
Definition: svdglue.cxx:47
OUString GetName() const
Definition: svdobj.cxx:775
#define Y
virtual sal_uInt32 GetHdlCount() const override
Via GetHdlCount the number of Handles can be retrieved.
Definition: svdoedge.cxx:1741
virtual void NbcShear(const Point &rRef, Degree100 nAngle, double tn, bool bVShear) override
Definition: svdoedge.cxx:2368
virtual SdrObjectUniquePtr getFullDragClone() const override
Definition: svdoedge.cxx:1856
sal_uInt16 FindGluePoint(sal_uInt16 nId) const
Definition: svdglue.cxx:331
virtual SdrLayerID GetLayer() const
Definition: svdobj.cxx:651
tools::Long AdjustTop(tools::Long nVertMoveDelta)
Point PixelToLogic(const Point &rDevicePt) const
SdrEdgeInfoRec aEdgeInfo
Definition: svdoedge.hxx:119
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:135
bool IsSet(SdrLayerID a) const
Definition: svdsob.hxx:66
void Insert(sal_uInt16 nPos, const Point &rPt, PolyFlags eFlags)
Definition: _xpoly.cxx:352
sal_uInt16 nConId
Definition: svdoedge.hxx:46
virtual SdrGluePoint GetCornerGluePoint(sal_uInt16 nNum) const
Definition: svdobj.cxx:2251
void RotatePoint(Point &rPnt, const Point &rRef, double sn, double cs)
Definition: svdtrans.hxx:115
sal_uInt32 count() const
std::unique_ptr< XPolygon > pEdgeTrack
Definition: svdoedge.hxx:144
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:2430
virtual void AddToHdlList(SdrHdlList &rHdlList) const override
Definition: svdoedge.cxx:1770
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdoedge.cxx:2474
OUString ImpGetDescriptionStr(const char *pStrCacheID) const
Definition: svdobj.cxx:1076
virtual bool applySpecialDrag(SdrDragStat &rDrag) override
Definition: svdoedge.cxx:1883
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:1985
bool IsBestConnection() const
Definition: svdoedge.hxx:64
void RemoveListener(SfxListener &rListener)
Definition: svdobj.cxx:691
SdrObjConnection aCon1
Definition: svdoedge.hxx:141
virtual void RestoreGeoData(const SdrObjGeoData &rGeo) override
Definition: svdotext.cxx:1451
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:95
PointerStyle
bool mbBoundRectCalculationRunning
Definition: svdoedge.hxx:160
virtual void SetRectsDirty(bool bNotMyself=false, bool bRecursive=true)
Definition: svdobj.cxx:509
void SetConnectMarker(const SdrObjConnection &rCon)
Definition: svdcrtv.cxx:269
void SetEdgeTrackDirty()
Definition: svdoedge.hxx:212
sal_uInt16 ImpGetPolyIdx(SdrEdgeLineCode eLineCode, const XPolygon &rXP) const
Definition: svdoedge.cxx:100
sal_uInt16 GetMarkHdlSizePixel() const
Definition: svdmrkv.cxx:2039
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:1709
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:2582
tools::Long AdjustLeft(tools::Long nHorzMoveDelta)
static SdrEscapeDirection ImpCalcEscAngle(SdrObject const *pObj, const Point &aPt2)
Definition: svdoedge.cxx:621
virtual void NbcSetAnchorPos(const Point &rPnt)
Definition: svdobj.cxx:1618
bool LineGeometryUsageIsNecessary() const
Definition: svdobj.cxx:1039
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:2952
SdrCreateCmd
Definition: svdtypes.hxx:27
sal_uInt16 GetPointCount() const
Definition: _xpoly.cxx:346
virtual void SetChanged()
Definition: svdobj.cxx:1006
void MoveXPoly(XPolygon &rPoly, const Size &S)
Definition: svdtrans.cxx:33
Point aObj1Line2
Definition: svdoedge.hxx:80
tools::Long Right() const
void SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle &rBoundRect) const
Definition: svdobj.cxx:2718
void SetPos(const Point &rNewPos)
Definition: svdglue.hxx:84
bool GetSuppressDefaultConnect() const
Definition: svdoedge.hxx:169
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:2093
void HideConnectMarker()
Definition: svdcrtv.cxx:292
Utility class SdrEdgeObj.
Definition: svdoedge.hxx:128
SdrObject * pObj
Definition: svdoedge.hxx:45
virtual void RecalcSnapRect() override
Snap is not done on the BoundRect but if possible on logic coordinates (i.e.
Definition: svdoedge.cxx:418
virtual Point GetSnapPoint(sal_uInt32 i) const override
Definition: svdoedge.cxx:2417